Introduction

In this notebook we demonstrate how to use Milo to detect abherrant cell states in diseased tissues, using a dataset of hepatic non-parenchymal cells isolated from 5 healthy and 5 cirrhotic human livers. Ramachandran et al. 2019 (GEO accessiion: GSE136103).

# devtools::install_github("MarioniLab/miloR")
suppressPackageStartupMessages({
  library(tidyverse)
  # library(irlba)
  # library(DropletUtils)
  library(scater)
  library(scran)
  # library(Seurat) ## just 4 loading the object
  library(miloR)
  library(SingleCellExperiment)
  library(patchwork)
  library(igraph)
  library(RColorBrewer)
  library(cowplot)
  })

Load data

We downloaded the dataset and annotations stored in Seurat object from here, as indicated by the authors.

load("/nfs/team205/ed6/data/Ramachandran2019_liver/tissue.rdata")

## Convert to SingleCellExperiment
liver_sce <- SingleCellExperiment(assay = list(counts=tissue@raw.data, logcounts=tissue@data),
                                  colData = tissue@meta.data)

liver_sce

Preprocessing

We use the same number of highly variable genes and principal components used by the authors of the original study.

Feature selection

Select highly variable genes

dec_liver <- modelGeneVar(liver_sce)

fit_liver <- metadata(dec_liver)
plot(fit_liver$mean, fit_liver$var, xlab="Mean of log-expression",
    ylab="Variance of log-expression")

hvgs <- getTopHVGs(dec_liver, n=3000)

Dimensionality reduction

set.seed(42)
liver_sce <- runPCA(liver_sce, subset_row=hvgs, ncomponents=11)
liver_sce <- runUMAP(liver_sce, dimred="PCA", ncomponents=2)

scater::plotUMAP(liver_sce, colour_by="condition", point_alpha=1,  point_size=0.5)
scater::plotUMAP(liver_sce, colour_by="dataset", point_alpha=0.3,  point_size=0.5)
scater::plotUMAP(liver_sce, colour_by="annotation_lineage", point_alpha=0.3,  point_size=0.5, text_by='annotation_lineage')

Notably, this dataset doesn’t appear to display a batch effect

Differential Abundance analysis with Milo

We test for differential abundance between healthy and cirrhotic livers. We start by defining neighbourhoods with refined sampling on the KNN graph. We inspect the size of neighbourhoods.

liver_milo <- Milo(liver_sce)

## Build KNN graph
liver_milo <- buildGraph(liver_milo, d = 11, k=30)
Constructing kNN graph with k:30
## Compute neighbourhoods with refined sampling
liver_milo <- makeNhoods(liver_milo, k=30, d=11, prop = 0.05, refined=TRUE)
Checking valid object
plotNhoodSizeHist(liver_milo, bins=150)

Then we make a design matrix for the differential test, assigning samples to biological conditions.

colData(liver_milo)[['sort']] <- str_remove(colData(liver_milo)[['dataset']], ".+_")
colData(liver_milo)[['sort']] <- str_remove(colData(liver_milo)[['sort']], "A|B")

liver_meta <- as.tibble(colData(liver_milo)[,c("dataset","condition", 'sort')])
`as.tibble()` was deprecated in tibble 2.0.0.
Please use `as_tibble()` instead.
The signature and semantics have changed, see `?as_tibble`.
liver_meta <- distinct(liver_meta) %>%
  mutate(condition=factor(condition, levels=c("Uninjured", "Cirrhotic"))) %>%
  column_to_rownames("dataset")

Now we can count cells in neighbourhoods and perform the DA test.

liver_milo <- countCells(liver_milo, samples = "dataset", meta.data = data.frame(colData(liver_milo)[,c("dataset","condition",'sort')]) )
Checking meta.data validity
Counting cells in neighbourhoods
liver_milo <- calcNhoodDistance(liver_milo, d=11)
milo_res <- testNhoods(liver_milo, design = ~ condition, design.df = liver_meta[colnames(nhoodCounts(liver_milo)),])
Using TMM normalisation
Performing spatial FDR correction withk-distance weighting
milo_res_sort <- testNhoods(liver_milo, design = ~ sort + condition, design.df = liver_meta[colnames(nhoodCounts(liver_milo)),])
Using TMM normalisation
Performing spatial FDR correction withk-distance weighting
compare_da_df <- left_join(milo_res_sort, milo_res, by="Nhood", suffix=c("_sort", "_nosort")) %>%
  {annotateNhoods(liver_milo, ., 'annotation_lineage')} 

compare_da_df %>%
  ggplot(aes(-log10(SpatialFDR_sort), -log10(SpatialFDR_nosort))) +
  geom_point(size=0.8) +
  geom_point(data=. %>% filter(annotation_lineage=="Endothelia"), color="red")

plot(milo_res_sort$SpatialFDR, milo_res$SpatialFDR)

Exploration of Milo DA results

We can start by looking at some basic stats

pval_hist <- milo_res %>%
  ggplot(aes(PValue)) +
  geom_histogram(bins=50) +
  theme_bw(base_size=14)

volcano <-
  milo_res %>%
  ggplot(aes(logFC, -log10(SpatialFDR))) +
  geom_point(size=0.4, alpha=0.2) +
  geom_hline(yintercept = -log10(0.1)) +
  xlab("log-Fold Change") +
  theme_bw(base_size=14)

pval_hist + volcano

The distribution of P-values looks sensible and from the volcano plot we can see that we have identified some DA neighbourhoods at 10% FDR.

We can visualize DA neighbourhoods building an abstracted graph

liver_milo <- buildNhoodGraph(liver_milo)
plotNhoodGraphDA(liver_milo, milo_res, alpha = 0.1, size_range=c(2,6))

## Save milo object and results
saveRDS(liver_milo,"/nfs/team205/ed6/data/Ramachandran2019_liver/liver_milo_20210225.RDS")
write_csv(milo_res,"/nfs/team205/ed6/data/Ramachandran2019_liver/liver_results_20210225.csv")
## Load hvgs 
hvgs <- scan("~/data/Ramachandran2019_liver/liver_milo_hvgs.txt", "")
Read 3000 items

Making figures for the manuscript

library(ggrastr)
colourCount = length(unique(liver_milo$annotation_lineage))
getPalette = colorRampPalette(brewer.pal(9, "Set2"))
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
umap_df <- data.frame(reducedDim(liver_milo, "UMAP"))
colnames(umap_df) <- c("UMAP_1", "UMAP_2")

umap1 <- cbind(umap_df, annotation_lineage=liver_milo$annotation_lineage) %>%
  ggplot(aes(UMAP_1, UMAP_2, color=as.character(annotation_lineage))) +
  geom_point_rast(size=0.1, alpha=0.5, raster.dpi = 800) +
  ggrepel::geom_text_repel(data = . %>%
              group_by(annotation_lineage) %>%
              summarise(UMAP_1=mean(UMAP_1), UMAP_2=mean(UMAP_2)),
            aes(label=annotation_lineage), color="black", size=6
            ) +
  scale_color_manual(values=getPalette(colourCount)) +
  guides(color="none") +
  xlab("UMAP1") + ylab("UMAP2") +
  coord_fixed() +
  theme_classic(base_size = 22) +
  theme(axis.text = element_blank(), axis.ticks = element_blank())

umap2 <-
  cbind(umap_df, condition=as.character(liver_milo$condition)) %>%
  ggplot(aes(UMAP_1, UMAP_2, color=condition)) +
  geom_point_rast(size=0.1, alpha=0.5, raster.dpi = 800) +
  scale_color_brewer(palette="Set1", name='') +
  xlab("UMAP1") + ylab("UMAP2") +
  coord_fixed() +
  guides(color='none') +
  facet_wrap(condition~., ncol=1) +
  theme_nothing(font_size = 22) +
  theme(axis.text = element_blank(), axis.ticks = element_blank(), legend.position=c(0.9,0.9),
        strip.background = element_rect(color=NA), strip.text = element_text(size=22))

nh_graph_pl <- plotNhoodGraphDA(liver_milo, milo_res, alpha = 0.1, size_range=c(1,4)) +
  theme(legend.text = element_text(size=20), legend.title = element_text(size=22)) +
  coord_fixed()

nh_graph_pl + ggsave("~/mount/gdrive/milo/Figures/liver_v2/liver_graph.pdf", height = 7, width = 8)


fig4_top <- (umap1 | umap2 | nh_graph_pl) +
  plot_layout(widths = c(3,1,3))

fig4_top

Explore DA neighbourhoods by cell type

Next, we can check the cell types where we observe most differences between healthy and cirrhotic cells, by taking the most frequent cell type in each neighbourhood.

milo_res <- milo_res[,!str_detect(colnames(milo_res), "annotation_lineage")]

# Add annotation of most frequent cell type per nhood to milo results table
milo_res <- annotateNhoods(liver_milo, milo_res, "annotation_indepth")
anno_df <- data.frame(liver_milo@colData) %>%
  distinct(annotation_lineage, annotation_indepth)
milo_res <- left_join(milo_res, anno_df, by="annotation_indepth")

We first check that neighbourhoods are sufficiently homogeneous

frac_hist <- ggplot(milo_res, aes(annotation_indepth_fraction)) +
  geom_histogram(bins=30) +
  xlab("Fraction of cells in \nmost abundant cluster") +
  ylab("# neighbourhoods") +
  theme_bw(base_size=14)

frac_hist

Filter nhoods with homogeneous composition

milo_res$annotation_indepth[milo_res$annotation_indepth_fraction < 0.6] <- NA
milo_res$annotation_lineage[milo_res$annotation_indepth_fraction < 0.6] <- NA

I can recover all the clusters where DA was detected in the original paper

group.by = "annotation_indepth"
paper_DA <- list(cirrhotic=c("MPs (4)","MPs (5)",
                             "Endothelia (6)", "Endothelia (7)",
                             "Mes (3)",
                             "Tcells (2)",
                             "Myofibroblasts"
                             ),
                 healthy=c("MPs (7)",
                           "Endothelia (1)",
                           "Tcells (1)", "Tcells (3)","Tcells (1)",
                           "ILCs (1)"
                           )
                 )

expDA_df <- bind_rows(
  data.frame(annotation_indepth = paper_DA[["cirrhotic"]], pred_DA="cirrhotic"),
  data.frame(annotation_indepth = paper_DA[["healthy"]], pred_DA="healthy")
  )

pl1 <- milo_res %>%
  left_join(expDA_df) %>%
  mutate(is_signif = ifelse(SpatialFDR < 0.1, 1, 0)) %>%
  mutate(logFC_color = ifelse(is_signif==1, logFC, NA)) %>%
  arrange(annotation_lineage) %>%
  mutate(Nhood=factor(Nhood, levels=unique(Nhood))) %>%
  filter(!is.na(annotation_lineage)) %>%
  ggplot(aes(annotation_indepth, logFC, color=logFC_color)) +
  scale_color_gradient2() +
  guides(color="none") +
  xlab(group.by) + ylab("Log Fold Change") +
  ggbeeswarm::geom_quasirandom(alpha=1) +
  coord_flip() +
  facet_grid(annotation_lineage~., scales="free", space="free") +
  theme_bw(base_size=22) +
  theme(strip.text.y =  element_text(angle=0),
        axis.title.y = element_blank(), axis.text.y = element_blank(), axis.ticks.y = element_blank(),
        )

pl2 <- milo_res %>%
  left_join(expDA_df) %>%
  # dplyr::filter(!is.na(pred_DA)) %>%
  group_by(annotation_indepth) %>%
  summarise(pred_DA=dplyr::first(pred_DA), annotation_lineage=dplyr::first(annotation_lineage)) %>%
  mutate(end=ifelse(pred_DA=="healthy", 0, 1),
         start=ifelse(pred_DA=="healthy", 1, 0)) %>%
  filter(!is.na(annotation_lineage)) %>%
  ggplot(aes(annotation_indepth, start, xend = annotation_indepth, yend = end, color=pred_DA)) +
  geom_segment(size=1,arrow=arrow(length = unit(0.1, "npc"), type="closed")) +
  coord_flip() +
  xlab("annotation") +
  facet_grid(annotation_lineage~.,
    # annotation_lineage~"Ramachandran et al.\nDA predictions",
             scales="free", space="free") +
  # guides(color="none") +
  scale_color_brewer(palette="Set1", direction = -1,
                     labels=c("enriched in cirrhotic", "enriched in healthy"),
                     na.translate = F,
                     name="Ramachandran et al.\nDA predictions") +
  guides(color=guide_legend(ncol = 1)) +
  theme_bw(base_size=22) +
  ylim(-0.1,1.1) +
  theme(strip.text.y = element_blank(),strip.text.x = element_text(angle=90),
        plot.margin = unit(c(0,0,0,0), "cm"), panel.grid = element_blank(),
        axis.title.x = element_blank(), axis.text.x = element_blank(), axis.ticks.x = element_blank(),
        legend.position = "bottom")

fig4_bleft <- (pl2 + pl1 +
  plot_layout(widths=c(1,10), guides = "collect") & theme(legend.position = 'top', legend.justification = 0))

fig4_bleft +
  ggsave("~/mount/gdrive/milo/Figures/liver_v2/liver_DAcomparison.pdf", width=8, height = 13)

Close-up on Endothelial lineage

endo_milo <- scater::runUMAP(liver_milo[,liver_milo$annotation_lineage=="Endothelia"],  dimred='PCA')
plotUMAP(endo_milo, colour_by = "annotation_indepth")

umap_df <- data.frame(reducedDim(endo_milo, "UMAP"))
colnames(umap_df) <- c("UMAP_1", "UMAP_2")

endo_umap <- cbind(umap_df, condition=endo_milo$condition) %>%
   ggplot(aes(UMAP_1, UMAP_2, color=condition)) +
  geom_point(size=0.3, alpha=0.5) +
  scale_color_brewer(palette="Set1", name='') +
  xlab("UMAP1") + ylab("UMAP2") +
  coord_fixed() +
  guides(color="none") +
  facet_wrap(condition~., ncol=1) +
  theme_nothing() +
  theme(axis.text = element_blank(), axis.ticks = element_blank(), legend.position=c(0.9,0.9),
        strip.background = element_rect(color=NA), strip.text = element_text(size=22))
liver_milo2 <- liver_milo
subset.nhoods <- str_detect(milo_res$annotation_indepth, "Endo")
reducedDim(liver_milo2, "UMAP")[colnames(endo_milo),] <- reducedDim(endo_milo, "UMAP") 

endo_gr <-
  plotNhoodGraphDA(
  liver_milo2, milo_res,
  subset.nhoods = which(milo_res$annotation_lineage == "Endothelia"), 
  size_range=c(1,4),
  # ) =)[1:(length()-1)], 
  alpha = 0.1
  )  +
   theme(legend.text = element_text(size=20), legend.title = element_text(size=22))
  
# liver_milo2 <- liver_milo
# subset.nhoods <- str_detect(milo_res$annotation_indepth, "Endo")
# reducedDim(liver_milo2, "UMAP")[colnames(endo_milo),] <- reducedDim(endo_milo, "UMAP") 
# endo_gr_groups <- plotNhoodGroups(liver_milo2, milo_res_endogroups[milo_res_endogroups$annotation_lineage=="Endothelia",], 
#                 show_groups = c("54", "70"),
#                 size_range=c(1,4),
#                 subset.nhoods = milo_res_endogroups$annotation_lineage=="Endothelia") +
#   scale_fill_manual(values=c("54"=brewer.pal(4, "Spectral")[2], "70"=brewer.pal(4, "Spectral")[3]), 
#                     labels=c("54"="Uninjured group", '70'= "Cirrhotic group"),
#                     na.value="white",
#                     name = "Nhood group"
#                     ) +
#   theme(legend.text = element_text(size=20), legend.title = element_text(size=22))

fig4_bright1 <- ((endo_umap + endo_gr ) + 
  plot_layout(widths = c(1,2), 
                guides = "collect"
                )) 
fig4_bright1

Close-up on Cholangiocytes

chol_milo <- scater::runUMAP(liver_milo[,liver_milo$annotation_lineage=="Cholangiocytes"],  dimred='PCA')
plotUMAP(chol_milo, colour_by = "annotation_indepth")


plotUMAP(chol_milo, colour_by = "percent.mito")

Filter out cells that show contamination from immune cells (expression of immune markers)

keep <- logcounts(chol_milo)["CD19",] == 0 | logcounts(chol_milo)["MS4A1",] == 0
chol_milo <- chol_milo[,keep]
chol_milo <- scater::runUMAP(chol_milo,  dimred='PCA')

plotUMAP(chol_milo, colour_by = "annotation_indepth")

umap_df <- data.frame(reducedDim(chol_milo, "UMAP"))
colnames(umap_df) <- c("UMAP_1", "UMAP_2")

chol_umap <- cbind(umap_df, condition=chol_milo$condition) %>%
   ggplot(aes(UMAP_1, UMAP_2, color=condition)) +
  geom_point(size=0.3, alpha=0.5) +
  scale_color_brewer(palette="Set1", name='') +
  xlab("UMAP1") + ylab("UMAP2") +
  coord_fixed() +
  guides(color="none") +
  facet_wrap(condition~., ncol=1) +
  theme_nothing() +
  theme(axis.text = element_blank(), axis.ticks = element_blank(), legend.position=c(0.9,0.9),
        strip.background = element_rect(color=NA), strip.text = element_text(size=22))

chol_umap

liver_milo2 <- liver_milo
subset.nhoods <- milo_res$annotation_lineage=="Cholangiocytes"
reducedDim(liver_milo2, "UMAP")[colnames(chol_milo),] <- reducedDim(chol_milo, "UMAP") 

chol_gr <-
  plotNhoodGraphDA(
  liver_milo2, milo_res,
  subset.nhoods = subset.nhoods,
  size_range=c(2,5),
  # ) =)[1:(length()-1)], 
  alpha = 0.1
  )  +
   theme(legend.text = element_text(size=22), legend.title = element_text(size=24))
  
(chol_umap + chol_gr ) + 
  plot_layout(widths = c(1,2), 
                guides = "collect"
                )

# fig4_bright1 +
#   ggsave("~/milo_output/liver_endoGraph.pdf", width=9, height = 5)  

Differential Gene Expression analysis

In a subset of lineages, we want to test for differential expression between neighbourhoods enriched in cirrhotic cells and neighbourhoods enriched

Add nhood expression to speed-up plotting of heatmaps

liver_milo <- calcNhoodExpression(liver_milo, assay = "logcounts", subset.row = hvgs)

Endothelia

Rebuttal figure showcasing grouping

p3 <- plotNhoodExpressionGroups(liver_milo, milo_res_endogroups, features = unique(tcell_marker_genes), 
                          subset.nhoods = milo_res_endogroups$NhoodGroup %in% c("3","10", "14"),
                          scale=TRUE, cluster_features = TRUE,show_rownames = TRUE
                          ) +
  theme(strip.text.x = element_text(angle=90))
Not all features in nhoodExpression(x): recomputing for requested features...

Group endothelial cells by logFC and DA results

milo_res_endogroups$annotation_indepth[milo_res_endogroups$annotation_indepth_fraction < 0.6] <- NA
milo_res_endogroups$annotation_lineage[milo_res_endogroups$annotation_indepth_fraction < 0.6] <- NA

## Group neighbourhoods by DA outcome
milo_res_endogroups$NhoodGroup <- NA
milo_res_endogroups$NhoodGroup <- ifelse((milo_res_endogroups$annotation_lineage == "Endothelia") & (milo_res_endogroups$SpatialFDR < 0.1) & (milo_res_endogroups$logFC < -2.5), "54", milo_res_endogroups$NhoodGroup)
milo_res_endogroups$NhoodGroup <- ifelse((milo_res_endogroups$annotation_lineage == "Endothelia") & (milo_res_endogroups$SpatialFDR < 0.1) & (milo_res_endogroups$logFC > 2.5), "70", milo_res_endogroups$NhoodGroup)


liver_milo2 <- liver_milo
subset.nhoods <- str_detect(milo_res$annotation_indepth, "Endo")
reducedDim(liver_milo2, "UMAP")[colnames(endo_milo),] <- reducedDim(endo_milo, "UMAP") 
endo_gr_groups <- plotNhoodGroups(liver_milo2, milo_res_endogroups[milo_res_endogroups$annotation_lineage=="Endothelia",], 
                show_groups = c("54", "70"),
                size_range=c(1,4),
                subset.nhoods = milo_res_endogroups$annotation_lineage=="Endothelia") +
  scale_fill_manual(values=c("54"=brewer.pal(4, "Spectral")[2], "70"=brewer.pal(4, "Spectral")[3]), 
                    labels=c("54"="Uninjured group", '70'= "Cirrhotic group"),
                    na.value="white",
                    name = "Nhood group"
                    ) +
  theme(legend.text = element_text(size=20), legend.title = element_text(size=22))
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the existing scale.
fig4_bright1 <- ((endo_umap + endo_gr) + 
  plot_layout(widths = c(1,2), guides="collect"
                )) &
  theme(legend.box = "horizontal", legend.position = "top", legend.direction = "vertical")
fig4_bright1

Calculate marker genes between the two groups

mito_genes <- str_detect(hvgs, "^MT-")
markers_df <- findNhoodGroupMarkers(liver_milo, da.res = milo_res_endogroups, assay="counts",
                      subset.nhoods = (milo_res_endogroups$NhoodGroup %in% c("54", "70")),
                      subset.groups = c("54", "70"),
                      subset.row = hvgs[!mito_genes],
                      aggregate.samples = TRUE, sample_col = "dataset"
                      )

milo_res_endogroups[milo_res_endogroups$NhoodGroup %in% c("54", "70"),]

colnames(markers_df) <- str_replace(colnames(markers_df), "70", "cirr")
colnames(markers_df) <- str_replace(colnames(markers_df), "54", "uninj")

Visualize as volcano


highlight_genes <- c("PLVAP", "VWA1", "ACKR1", "IL32",
                     "CLEC4G", "CLEC4M", "FCN2", "FCN3",
                     "LEF1")

marker.df <- markers_df
marker.df %>%
  mutate(label=ifelse(GeneID %in% highlight_genes, GeneID, NA)) %>%
  ggplot(aes(logFC_cirr, -log10(adj.P.Val_cirr), 
             # color=highlight
             )) + 
  geom_point() +
  geom_text(aes(label=label), color="red") +
  xlab("logFC") + ylab("- log10(Adj. P value)") +
  theme_bw(base_size = 22)

NA

Visualize as heatmap

(gene expression values are scaled between 0 and 1 for each gene)

marker_genes <- marker.df %>%
  dplyr::filter(adj.P.Val_cirr < 0.05) %>%
  pull(GeneID)

fig4_bbright <-
  plotNhoodExpressionDA(liver_milo, milo_res_endogroups, c(marker_genes), cluster_features = TRUE, assay = "counts",
                      alpha = 0.1,
                      scale_to_1 = TRUE,
                      subset.nhoods =  milo_res_endogroups$NhoodGroup %in% c("54", "70"),
                      # grid.space = "free",
                      highlight_features = highlight_genes, show_rownames = FALSE
                      ) +
  ylab("DE genes")+
  # facet_grid(.~NhoodGroup, scales="free", space="free")
   theme(legend.text = element_text(size=22), legend.title = element_text(size=24)) +
  plot_layout(heights = c(1,10)) & theme(legend.margin = margin(0,0,0,60), legend.background = element_blank())

  
pl3 <- fig4_bbright$data %>%
  ggplot(aes(logFC_rank, 1,fill=logFC)) +
  geom_tile() +
      theme_classic(base_size=16) +
    ylab("") +
  scale_fill_gradient2(name="DA logFC") +
    # scale_fill_manual(values=c("54"=brewer.pal(4, "Spectral")[2], "70"=brewer.pal(4, "Spectral")[3]), 
    #                 labels=c("54"="Uninjured group", '70'= "Cirrhotic group"),
    #                 na.value="white",
    #                 name = "Nhood group"
    #                 ) +
    scale_x_continuous(expand = c(0.01, 0)) +
    theme(axis.text = element_blank(), axis.ticks = element_blank(), axis.line = element_blank(), 
          axis.title = element_blank())

fig4_bbright <- pl3 / fig4_bbright  +
  plot_layout(heights = c(1,20))

fig4_bbright

GO term analysis

go_endo_up <- em_res_up %>%
  top_n(30, -log10(qvalue)) %>%
   mutate(ID=ifelse(ID=='GO_ANTIGEN_PROCESSING_AND_PRESENTATION_OF_PEPTIDE_OR_POLYSACCHARIDE_ANTIGEN_VIA_MHC_CLASS_II', "GO_ANTIGEN_PRESENTATION_VIA_MHC_CLASS_II", ID)) %>%
  mutate(Term=factor(ID, levels=rev(unique(ID)))) %>%
  ggplot(aes(Term, -log10(qvalue))) +
  geom_point() +
  coord_flip() +
  xlab("GO Biological Function") + ylab("-log10(Adj. p-value)") +
  theme_bw(base_size=18) +
  ggtitle("Cirrhotic endothelia")

go_endo_down <- em_res_down %>%
  top_n(30, -log10(qvalue)) %>%
  mutate(ID=ifelse(ID=='GO_ANTIGEN_PROCESSING_AND_PRESENTATION_OF_PEPTIDE_OR_POLYSACCHARIDE_ANTIGEN_VIA_MHC_CLASS_II', "GO_ANTIGEN_PRESENTATION_VIA_MHC_CLASS_II", ID)) %>%
  mutate(Term=factor(ID, levels=rev(unique(ID)))) %>%
  ggplot(aes(Term, -log10(qvalue))) +
  geom_point() +
  coord_flip() +
  xlab("GO Biological Function") + ylab("-log10(Adj. p-value)") +
  theme_bw(base_size=18) +
  ggtitle("Uninjured endothelia")

go_endo_up

go_endo_down

em_res_up
em_res_down

Cholangiocytes

set.seed(42)
milo_res_cholgroups <- groupNhoods(liver_milo, milo_res, max.lfc.delta = 0.5, overlap = 1)
Found 1351 DA neighbourhoods at FDR 10%
nhoodAdjacency found - using for nhood grouping
## Group neighbourhoods by DA outcome
milo_res_cholgroups$NhoodGroup <- NA
milo_res_cholgroups$NhoodGroup <- ifelse((milo_res_cholgroups$annotation_lineage == "Cholangiocytes") & (milo_res_cholgroups$SpatialFDR < 0.1) & (milo_res_cholgroups$logFC < -2.5), "38", milo_res_cholgroups$NhoodGroup)
milo_res_cholgroups$NhoodGroup <- ifelse((milo_res_cholgroups$annotation_lineage == "Cholangiocytes") & (milo_res_cholgroups$SpatialFDR < 0.1) & (milo_res_cholgroups$logFC > 2.5), "49", milo_res_cholgroups$NhoodGroup)

liver_milo2 <- liver_milo
subset.nhoods <- str_detect(milo_res$annotation_indepth, "Chol")
reducedDim(liver_milo2, "UMAP")[colnames(chol_milo),] <- reducedDim(chol_milo, "UMAP") 
plotNhoodGroups(liver_milo2, milo_res_cholgroups[milo_res_cholgroups$annotation_lineage=="Cholangiocytes",], 
                show_groups = c("49","38"),
                subset.nhoods =  milo_res_cholgroups$annotation_lineage =="Cholangiocytes")

Calculate marker genes between the two groups

## Filter genes expressed in cholangiocytes
# chol_hvgs <- hvgs[(counts(chol_milo)[hvgs,] > 0) %>% {rowSums(.)/ncol(chol_milo)} > 0.01]
mito_genes <- str_detect(hvgs, "^MT-")

markers_df <- findNhoodGroupMarkers(liver_milo, da.res = milo_res_cholgroups, assay="counts",
                      subset.nhoods = milo_res_cholgroups$NhoodGroup %in%c("49","38"),
                      subset.groups = c("49","38"),
                      subset.row = hvgs[!mito_genes],
                      aggregate.samples = TRUE, sample_col = "dataset"
                      )

markers_df 

milo_res_cholgroups[milo_res_cholgroups$NhoodGroup %in%c("49","38"),]

Visualize as volcano

marker.df.chol <- markers_df

volcano_chol <-
  marker.df.chol %>%
  mutate(up=ifelse(logFC_49 > 0, "up", "down")) %>%
  group_by(up) %>%
  mutate(label=ifelse(rank(adj.P.Val_49) < 15, GeneID, NA)) %>%
  # mutate(label=ifelse((adj.P.Val_49 < 0.05 & logFC_49 < -3) | (adj.P.Val_49 < 0.05 & logFC_49 > 0), GeneID, NA)) %>%
  ggplot(aes(logFC_49, -log10(adj.P.Val_49), 
             # color=highlight
             )) + 
  geom_point(size=0.8, alpha=0.6) +
  ggrepel::geom_text_repel(aes(label=label), segment.alpha = 0.2) +
  xlab("logFC") + ylab("- log10(Adj. P value)") +
  theme_bw(base_size = 22)

volcano_chol  

NA

GO term analysis

go_chol_up <- em_res_up_chol %>%
  top_n(20, -log10(qvalue)) %>%
  mutate(Term=factor(ID, levels=rev(unique(ID)))) %>%
  ggplot(aes(Term, -log10(qvalue))) +
  geom_point() +
  coord_flip() +
  xlab("GO Biological Function") + ylab("-log10(Adj. p-value)") +
  theme_bw(base_size=18) +
  ggtitle("Cirrhotic cholangiocytes")

go_chol_up

em_res_up_chol

Assemble figure

Assemble supplementary figure

p1 <- plot_grid( go_endo_up+ theme(plot.title = element_text(hjust = 1),
                                   axis.title.x = element_text(hjust = 1)), 
                 go_endo_down+ theme(plot.title = element_text(hjust = 1),
                                     axis.title.x = element_text(hjust = 1)), 
                 label_size = 18,
                 ncol=1,
                 rel_heights = c(2,2),
                labels = c("A", "B","C"))

p1


chol_emb <- (chol_umap + chol_gr ) + 
  plot_layout(widths = c(1,2), 
                guides = "collect"
                )
plot_grid(
  go_endo_up+ theme(plot.title = element_text(hjust = 1),
                                   axis.title.x = element_text(hjust = 1)), 
                 go_endo_down+ theme(plot.title = element_text(hjust = 1),
                                     axis.title.x = element_text(hjust = 1)), 
                 label_size = 18,
                 ncol=1,
                 rel_heights = c(2,2), rel_widths = c(2,2),
                labels = c("A", "B")
  ) +
  ggsave("~/mount/gdrive/milo/Figures/liver_v2/suppl_fig_endo.pdf", height = 12, width=12) +
  ggsave("~/mount/gdrive/milo/Figures/liver_v2/suppl_fig_endo.png", height = 12, width=12)

NA
NA
plot_grid(plot_grid(chol_umap, chol_gr, volcano_chol, nrow=1,rel_widths = c(1,2,2),
                          label_size = 18,
                labels = c("A","B","C")),
                go_chol_up + theme(plot.title = element_text(hjust = 1),
                                   axis.title.x = element_text(hjust = 1)), 
                ncol=1,
                rel_heights = c(1,1),
                 label_size = 18,
                labels=c("",'D')) +
   ggsave("~/mount/gdrive/milo/Figures/liver_v2/suppl_fig7.pdf", height = 13, width=14) +
  ggsave("~/mount/gdrive/milo/Figures/liver_v2/suppl_fig7.png", height = 13, width=14) 
Removed 2963 rows containing missing values (geom_text_repel).

LS0tCnRpdGxlOiAiTWlsbzogbGl2ZXIgY2lycmhvc2lzIGFuYWx5c2lzIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCgojIyBJbnRyb2R1Y3Rpb24KCkluIHRoaXMgbm90ZWJvb2sgd2UgZGVtb25zdHJhdGUgaG93IHRvIHVzZSBNaWxvIHRvIGRldGVjdCBhYmhlcnJhbnQgY2VsbCBzdGF0ZXMgaW4gZGlzZWFzZWQgdGlzc3VlcywgdXNpbmcgYSBkYXRhc2V0IG9mIGhlcGF0aWMgbm9uLXBhcmVuY2h5bWFsIGNlbGxzIGlzb2xhdGVkIGZyb20gNSBoZWFsdGh5IGFuZCA1IGNpcnJob3RpYyBodW1hbiBsaXZlcnMuIFtSYW1hY2hhbmRyYW4gZXQgYWwuIDIwMTldKGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNTg2LTAxOS0xNjMxLTMjU2VjMSkgKEdFTyBhY2Nlc3NpaW9uOiBHU0UxMzYxMDMpLgoKYGBge3J9CiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJNYXJpb25pTGFiL21pbG9SIikKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHsKICBsaWJyYXJ5KHRpZHl2ZXJzZSkKICAjIGxpYnJhcnkoaXJsYmEpCiAgIyBsaWJyYXJ5KERyb3BsZXRVdGlscykKICBsaWJyYXJ5KHNjYXRlcikKICBsaWJyYXJ5KHNjcmFuKQogICMgbGlicmFyeShTZXVyYXQpICMjIGp1c3QgNCBsb2FkaW5nIHRoZSBvYmplY3QKICBsaWJyYXJ5KG1pbG9SKQogIGxpYnJhcnkoU2luZ2xlQ2VsbEV4cGVyaW1lbnQpCiAgbGlicmFyeShwYXRjaHdvcmspCiAgbGlicmFyeShpZ3JhcGgpCiAgbGlicmFyeShSQ29sb3JCcmV3ZXIpCiAgbGlicmFyeShjb3dwbG90KQogIH0pCmBgYAoKIyMgTG9hZCBkYXRhCgpXZSBkb3dubG9hZGVkIHRoZSBkYXRhc2V0IGFuZCBhbm5vdGF0aW9ucyBzdG9yZWQgaW4gU2V1cmF0IG9iamVjdCBmcm9tIFtoZXJlXShodHRwczovL2RhdGFzaGFyZS5pcy5lZC5hYy51ay9oYW5kbGUvMTAyODMvMzQzMyksIGFzIGluZGljYXRlZCBieSB0aGUgYXV0aG9ycy4KCmBgYHtyfQpsb2FkKCIvbmZzL3RlYW0yMDUvZWQ2L2RhdGEvUmFtYWNoYW5kcmFuMjAxOV9saXZlci90aXNzdWUucmRhdGEiKQoKIyMgQ29udmVydCB0byBTaW5nbGVDZWxsRXhwZXJpbWVudApsaXZlcl9zY2UgPC0gU2luZ2xlQ2VsbEV4cGVyaW1lbnQoYXNzYXkgPSBsaXN0KGNvdW50cz10aXNzdWVAcmF3LmRhdGEsIGxvZ2NvdW50cz10aXNzdWVAZGF0YSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gdGlzc3VlQG1ldGEuZGF0YSkKCmxpdmVyX3NjZQpgYGAKCiMjIFByZXByb2Nlc3NpbmcKCldlIHVzZSB0aGUgc2FtZSBudW1iZXIgb2YgaGlnaGx5IHZhcmlhYmxlIGdlbmVzIGFuZCBwcmluY2lwYWwgY29tcG9uZW50cyB1c2VkIGJ5IHRoZSBhdXRob3JzIG9mIHRoZSBvcmlnaW5hbCBzdHVkeS4gCgojIyMgRmVhdHVyZSBzZWxlY3Rpb24KClNlbGVjdCBoaWdobHkgdmFyaWFibGUgZ2VuZXMKCmBgYHtyfQpkZWNfbGl2ZXIgPC0gbW9kZWxHZW5lVmFyKGxpdmVyX3NjZSkKCmZpdF9saXZlciA8LSBtZXRhZGF0YShkZWNfbGl2ZXIpCnBsb3QoZml0X2xpdmVyJG1lYW4sIGZpdF9saXZlciR2YXIsIHhsYWI9Ik1lYW4gb2YgbG9nLWV4cHJlc3Npb24iLAogICAgeWxhYj0iVmFyaWFuY2Ugb2YgbG9nLWV4cHJlc3Npb24iKQoKaHZncyA8LSBnZXRUb3BIVkdzKGRlY19saXZlciwgbj0zMDAwKQpgYGAKCiMjIyBEaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24KCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQpzZXQuc2VlZCg0MikKbGl2ZXJfc2NlIDwtIHJ1blBDQShsaXZlcl9zY2UsIHN1YnNldF9yb3c9aHZncywgbmNvbXBvbmVudHM9MTEpCmxpdmVyX3NjZSA8LSBydW5VTUFQKGxpdmVyX3NjZSwgZGltcmVkPSJQQ0EiLCBuY29tcG9uZW50cz0yKQoKc2NhdGVyOjpwbG90VU1BUChsaXZlcl9zY2UsIGNvbG91cl9ieT0iY29uZGl0aW9uIiwgcG9pbnRfYWxwaGE9MSwgIHBvaW50X3NpemU9MC41KQpzY2F0ZXI6OnBsb3RVTUFQKGxpdmVyX3NjZSwgY29sb3VyX2J5PSJkYXRhc2V0IiwgcG9pbnRfYWxwaGE9MC4zLCAgcG9pbnRfc2l6ZT0wLjUpCnNjYXRlcjo6cGxvdFVNQVAobGl2ZXJfc2NlLCBjb2xvdXJfYnk9ImFubm90YXRpb25fbGluZWFnZSIsIHBvaW50X2FscGhhPTAuMywgIHBvaW50X3NpemU9MC41LCB0ZXh0X2J5PSdhbm5vdGF0aW9uX2xpbmVhZ2UnKQpgYGAKCk5vdGFibHksIHRoaXMgZGF0YXNldCBkb2Vzbid0IGFwcGVhciB0byBkaXNwbGF5IGEgYmF0Y2ggZWZmZWN0CgpgYGB7cn0Kc2F2ZVJEUyhsaXZlcl9zY2UsICJ+L21vdW50L2dkcml2ZS9taWxvL2xpdmVyX1NDRV8yMDIxMDIyNS5SRFMiKQpsaXZlcl9zY2UgPC0gcmVhZFJEUygifi9tb3VudC9nZHJpdmUvbWlsby9saXZlcl9TQ0VfMjAyMTAyMjUuUkRTIikKYGBgCgojIyBEaWZmZXJlbnRpYWwgQWJ1bmRhbmNlIGFuYWx5c2lzIHdpdGggTWlsbwoKV2UgdGVzdCBmb3IgZGlmZmVyZW50aWFsIGFidW5kYW5jZSBiZXR3ZWVuIGhlYWx0aHkgYW5kIGNpcnJob3RpYyBsaXZlcnMuIFdlIHN0YXJ0IGJ5IGRlZmluaW5nIG5laWdoYm91cmhvb2RzIHdpdGggcmVmaW5lZCBzYW1wbGluZyBvbiB0aGUgS05OIGdyYXBoLiBXZSBpbnNwZWN0IHRoZSBzaXplIG9mIG5laWdoYm91cmhvb2RzLgoKYGBge3J9CmxpdmVyX21pbG8gPC0gTWlsbyhsaXZlcl9zY2UpCgojIyBCdWlsZCBLTk4gZ3JhcGgKbGl2ZXJfbWlsbyA8LSBidWlsZEdyYXBoKGxpdmVyX21pbG8sIGQgPSAxMSwgaz0zMCkKCiMjIENvbXB1dGUgbmVpZ2hib3VyaG9vZHMgd2l0aCByZWZpbmVkIHNhbXBsaW5nCmxpdmVyX21pbG8gPC0gbWFrZU5ob29kcyhsaXZlcl9taWxvLCBrPTMwLCBkPTExLCBwcm9wID0gMC4wNSwgcmVmaW5lZD1UUlVFKQpwbG90Tmhvb2RTaXplSGlzdChsaXZlcl9taWxvLCBiaW5zPTE1MCkKYGBgCgpUaGVuIHdlIG1ha2UgYSBkZXNpZ24gbWF0cml4IGZvciB0aGUgZGlmZmVyZW50aWFsIHRlc3QsIGFzc2lnbmluZyBzYW1wbGVzIHRvIGJpb2xvZ2ljYWwgY29uZGl0aW9ucy4KCmBgYHtyfQpjb2xEYXRhKGxpdmVyX21pbG8pW1snc29ydCddXSA8LSBzdHJfcmVtb3ZlKGNvbERhdGEobGl2ZXJfbWlsbylbWydkYXRhc2V0J11dLCAiLitfIikKY29sRGF0YShsaXZlcl9taWxvKVtbJ3NvcnQnXV0gPC0gc3RyX3JlbW92ZShjb2xEYXRhKGxpdmVyX21pbG8pW1snc29ydCddXSwgIkF8QiIpCgpsaXZlcl9tZXRhIDwtIGFzLnRpYmJsZShjb2xEYXRhKGxpdmVyX21pbG8pWyxjKCJkYXRhc2V0IiwiY29uZGl0aW9uIiwgJ3NvcnQnKV0pCmxpdmVyX21ldGEgPC0gZGlzdGluY3QobGl2ZXJfbWV0YSkgJT4lCiAgbXV0YXRlKGNvbmRpdGlvbj1mYWN0b3IoY29uZGl0aW9uLCBsZXZlbHM9YygiVW5pbmp1cmVkIiwgIkNpcnJob3RpYyIpKSkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJkYXRhc2V0IikKCmBgYAoKTm93IHdlIGNhbiBjb3VudCBjZWxscyBpbiBuZWlnaGJvdXJob29kcyBhbmQgcGVyZm9ybSB0aGUgREEgdGVzdC4KCmBgYHtyfQpsaXZlcl9taWxvIDwtIGNvdW50Q2VsbHMobGl2ZXJfbWlsbywgc2FtcGxlcyA9ICJkYXRhc2V0IiwgbWV0YS5kYXRhID0gZGF0YS5mcmFtZShjb2xEYXRhKGxpdmVyX21pbG8pWyxjKCJkYXRhc2V0IiwiY29uZGl0aW9uIiwnc29ydCcpXSkgKQpsaXZlcl9taWxvIDwtIGNhbGNOaG9vZERpc3RhbmNlKGxpdmVyX21pbG8sIGQ9MTEpCm1pbG9fcmVzIDwtIHRlc3ROaG9vZHMobGl2ZXJfbWlsbywgZGVzaWduID0gfiBjb25kaXRpb24sIGRlc2lnbi5kZiA9IGxpdmVyX21ldGFbY29sbmFtZXMobmhvb2RDb3VudHMobGl2ZXJfbWlsbykpLF0pCm1pbG9fcmVzX3NvcnQgPC0gdGVzdE5ob29kcyhsaXZlcl9taWxvLCBkZXNpZ24gPSB+IHNvcnQgKyBjb25kaXRpb24sIGRlc2lnbi5kZiA9IGxpdmVyX21ldGFbY29sbmFtZXMobmhvb2RDb3VudHMobGl2ZXJfbWlsbykpLF0pCmBgYAoKYGBge3J9CmNvbXBhcmVfZGFfZGYgPC0gbGVmdF9qb2luKG1pbG9fcmVzX3NvcnQsIG1pbG9fcmVzLCBieT0iTmhvb2QiLCBzdWZmaXg9YygiX3NvcnQiLCAiX25vc29ydCIpKSAlPiUKICB7YW5ub3RhdGVOaG9vZHMobGl2ZXJfbWlsbywgLiwgJ2Fubm90YXRpb25fbGluZWFnZScpfSAKCmNvbXBhcmVfZGFfZGYgJT4lCiAgZ2dwbG90KGFlcygtbG9nMTAoU3BhdGlhbEZEUl9zb3J0KSwgLWxvZzEwKFNwYXRpYWxGRFJfbm9zb3J0KSkpICsKICBnZW9tX3BvaW50KHNpemU9MC44KSArCiAgZ2VvbV9wb2ludChkYXRhPS4gJT4lIGZpbHRlcihhbm5vdGF0aW9uX2xpbmVhZ2U9PSJFbmRvdGhlbGlhIiksIGNvbG9yPSJyZWQiKQpwbG90KG1pbG9fcmVzX3NvcnQkU3BhdGlhbEZEUiwgbWlsb19yZXMkU3BhdGlhbEZEUikKYGBgCgoKIyMgRXhwbG9yYXRpb24gb2YgTWlsbyBEQSByZXN1bHRzCgpXZSBjYW4gc3RhcnQgYnkgbG9va2luZyBhdCBzb21lIGJhc2ljIHN0YXRzCgpgYGB7cn0KcHZhbF9oaXN0IDwtIG1pbG9fcmVzICU+JQogIGdncGxvdChhZXMoUFZhbHVlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnM9NTApICsKICB0aGVtZV9idyhiYXNlX3NpemU9MTQpCgp2b2xjYW5vIDwtCiAgbWlsb19yZXMgJT4lCiAgZ2dwbG90KGFlcyhsb2dGQywgLWxvZzEwKFNwYXRpYWxGRFIpKSkgKwogIGdlb21fcG9pbnQoc2l6ZT0wLjQsIGFscGhhPTAuMikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjEpKSArCiAgeGxhYigibG9nLUZvbGQgQ2hhbmdlIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xNCkKCnB2YWxfaGlzdCArIHZvbGNhbm8KYGBgCgpUaGUgZGlzdHJpYnV0aW9uIG9mIFAtdmFsdWVzIGxvb2tzIHNlbnNpYmxlIGFuZCBmcm9tIHRoZSB2b2xjYW5vIHBsb3Qgd2UgY2FuIHNlZSB0aGF0IHdlIGhhdmUgaWRlbnRpZmllZCBzb21lIERBIG5laWdoYm91cmhvb2RzIGF0IDEwJSBGRFIuCgpXZSBjYW4gdmlzdWFsaXplIERBIG5laWdoYm91cmhvb2RzIGJ1aWxkaW5nIGFuIGFic3RyYWN0ZWQgZ3JhcGgKCmBgYHtyLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9MTB9CmxpdmVyX21pbG8gPC0gYnVpbGROaG9vZEdyYXBoKGxpdmVyX21pbG8pCnBsb3ROaG9vZEdyYXBoREEobGl2ZXJfbWlsbywgbWlsb19yZXMsIGFscGhhID0gMC4xLCBzaXplX3JhbmdlPWMoMiw2KSkKYGBgCgpgYGB7cn0KIyMgU2F2ZSBtaWxvIG9iamVjdCBhbmQgcmVzdWx0cwpzYXZlUkRTKGxpdmVyX21pbG8sIi9uZnMvdGVhbTIwNS9lZDYvZGF0YS9SYW1hY2hhbmRyYW4yMDE5X2xpdmVyL2xpdmVyX21pbG9fMjAyMTAyMjUuUkRTIikKd3JpdGVfY3N2KG1pbG9fcmVzLCIvbmZzL3RlYW0yMDUvZWQ2L2RhdGEvUmFtYWNoYW5kcmFuMjAxOV9saXZlci9saXZlcl9yZXN1bHRzXzIwMjEwMjI1LmNzdiIpCmBgYAoKYGBge3J9CmxpdmVyX21pbG8gPC0gcmVhZFJEUygifi9saXZlcl9taWxvXzIwMjAxMDA4LlJEUyIpCm1pbG9fcmVzIDwtIHJlYWRfY3N2KCIvbmZzL3RlYW0yMDUvZWQ2L2RhdGEvUmFtYWNoYW5kcmFuMjAxOV9saXZlci9saXZlcl9yZXN1bHRzXzIwMjAxMDA4LmNzdiIpCgojIyBMb2FkIGh2Z3MgCmh2Z3MgPC0gc2Nhbigifi9kYXRhL1JhbWFjaGFuZHJhbjIwMTlfbGl2ZXIvbGl2ZXJfbWlsb19odmdzLnR4dCIsICIiKQpgYGAKCgpNYWtpbmcgZmlndXJlcyBmb3IgdGhlIG1hbnVzY3JpcHQKCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9MTB9CmxpYnJhcnkoZ2dyYXN0cikKY29sb3VyQ291bnQgPSBsZW5ndGgodW5pcXVlKGxpdmVyX21pbG8kYW5ub3RhdGlvbl9saW5lYWdlKSkKZ2V0UGFsZXR0ZSA9IGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg5LCAiU2V0MiIpKQoKdW1hcF9kZiA8LSBkYXRhLmZyYW1lKHJlZHVjZWREaW0obGl2ZXJfbWlsbywgIlVNQVAiKSkKY29sbmFtZXModW1hcF9kZikgPC0gYygiVU1BUF8xIiwgIlVNQVBfMiIpCgp1bWFwMSA8LSBjYmluZCh1bWFwX2RmLCBhbm5vdGF0aW9uX2xpbmVhZ2U9bGl2ZXJfbWlsbyRhbm5vdGF0aW9uX2xpbmVhZ2UpICU+JQogIGdncGxvdChhZXMoVU1BUF8xLCBVTUFQXzIsIGNvbG9yPWFzLmNoYXJhY3Rlcihhbm5vdGF0aW9uX2xpbmVhZ2UpKSkgKwogIGdlb21fcG9pbnRfcmFzdChzaXplPTAuMSwgYWxwaGE9MC41LCByYXN0ZXIuZHBpID0gODAwKSArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGEgPSAuICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGFubm90YXRpb25fbGluZWFnZSkgJT4lCiAgICAgICAgICAgICAgc3VtbWFyaXNlKFVNQVBfMT1tZWFuKFVNQVBfMSksIFVNQVBfMj1tZWFuKFVNQVBfMikpLAogICAgICAgICAgICBhZXMobGFiZWw9YW5ub3RhdGlvbl9saW5lYWdlKSwgY29sb3I9ImJsYWNrIiwgc2l6ZT02CiAgICAgICAgICAgICkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Z2V0UGFsZXR0ZShjb2xvdXJDb3VudCkpICsKICBndWlkZXMoY29sb3I9Im5vbmUiKSArCiAgeGxhYigiVU1BUDEiKSArIHlsYWIoIlVNQVAyIikgKwogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMjIpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpCgp1bWFwMiA8LQogIGNiaW5kKHVtYXBfZGYsIGNvbmRpdGlvbj1hcy5jaGFyYWN0ZXIobGl2ZXJfbWlsbyRjb25kaXRpb24pKSAlPiUKICBnZ3Bsb3QoYWVzKFVNQVBfMSwgVU1BUF8yLCBjb2xvcj1jb25kaXRpb24pKSArCiAgZ2VvbV9wb2ludF9yYXN0KHNpemU9MC4xLCBhbHBoYT0wLjUsIHJhc3Rlci5kcGkgPSA4MDApICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIsIG5hbWU9JycpICsKICB4bGFiKCJVTUFQMSIpICsgeWxhYigiVU1BUDIiKSArCiAgY29vcmRfZml4ZWQoKSArCiAgZ3VpZGVzKGNvbG9yPSdub25lJykgKwogIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4sIG5jb2w9MSkgKwogIHRoZW1lX25vdGhpbmcoZm9udF9zaXplID0gMjIpICsKICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbj1jKDAuOSwwLjkpLAogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3I9TkEpLCBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjIpKQoKbmhfZ3JhcGhfcGwgPC0gcGxvdE5ob29kR3JhcGhEQShsaXZlcl9taWxvLCBtaWxvX3JlcywgYWxwaGEgPSAwLjEsIHNpemVfcmFuZ2U9YygxLDQpKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSkgKwogIGNvb3JkX2ZpeGVkKCkKCm5oX2dyYXBoX3BsICsgZ2dzYXZlKCJ+L21vdW50L2dkcml2ZS9taWxvL0ZpZ3VyZXMvbGl2ZXJfdjIvbGl2ZXJfZ3JhcGgucGRmIiwgaGVpZ2h0ID0gNywgd2lkdGggPSA4KQoKZmlnNF90b3AgPC0gKHVtYXAxIHwgdW1hcDIgfCBuaF9ncmFwaF9wbCkgKwogIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMywxLDMpKQoKZmlnNF90b3AKYGBgCgojIyMgRXhwbG9yZSBEQSBuZWlnaGJvdXJob29kcyBieSBjZWxsIHR5cGUKCk5leHQsIHdlIGNhbiBjaGVjayB0aGUgY2VsbCB0eXBlcyB3aGVyZSB3ZSBvYnNlcnZlIG1vc3QgZGlmZmVyZW5jZXMgYmV0d2VlbiBoZWFsdGh5IGFuZCBjaXJyaG90aWMgY2VsbHMsIGJ5IHRha2luZyB0aGUgbW9zdCBmcmVxdWVudCBjZWxsIHR5cGUgaW4gZWFjaCBuZWlnaGJvdXJob29kLgoKYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTEwfQptaWxvX3JlcyA8LSBtaWxvX3Jlc1ssIXN0cl9kZXRlY3QoY29sbmFtZXMobWlsb19yZXMpLCAiYW5ub3RhdGlvbl9saW5lYWdlIildCgojIEFkZCBhbm5vdGF0aW9uIG9mIG1vc3QgZnJlcXVlbnQgY2VsbCB0eXBlIHBlciBuaG9vZCB0byBtaWxvIHJlc3VsdHMgdGFibGUKbWlsb19yZXMgPC0gYW5ub3RhdGVOaG9vZHMobGl2ZXJfbWlsbywgbWlsb19yZXMsICJhbm5vdGF0aW9uX2luZGVwdGgiKQphbm5vX2RmIDwtIGRhdGEuZnJhbWUobGl2ZXJfbWlsb0Bjb2xEYXRhKSAlPiUKICBkaXN0aW5jdChhbm5vdGF0aW9uX2xpbmVhZ2UsIGFubm90YXRpb25faW5kZXB0aCkKbWlsb19yZXMgPC0gbGVmdF9qb2luKG1pbG9fcmVzLCBhbm5vX2RmLCBieT0iYW5ub3RhdGlvbl9pbmRlcHRoIikKYGBgCgpXZSBmaXJzdCBjaGVjayB0aGF0IG5laWdoYm91cmhvb2RzIGFyZSBzdWZmaWNpZW50bHkgaG9tb2dlbmVvdXMKCmBgYHtyfQpmcmFjX2hpc3QgPC0gZ2dwbG90KG1pbG9fcmVzLCBhZXMoYW5ub3RhdGlvbl9pbmRlcHRoX2ZyYWN0aW9uKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnM9MzApICsKICB4bGFiKCJGcmFjdGlvbiBvZiBjZWxscyBpbiBcbm1vc3QgYWJ1bmRhbnQgY2x1c3RlciIpICsKICB5bGFiKCIjIG5laWdoYm91cmhvb2RzIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xNCkKCmZyYWNfaGlzdApgYGAKCkZpbHRlciBuaG9vZHMgd2l0aCBob21vZ2VuZW91cyBjb21wb3NpdGlvbgoKYGBge3J9Cm1pbG9fcmVzJGFubm90YXRpb25faW5kZXB0aFttaWxvX3JlcyRhbm5vdGF0aW9uX2luZGVwdGhfZnJhY3Rpb24gPCAwLjZdIDwtIE5BCm1pbG9fcmVzJGFubm90YXRpb25fbGluZWFnZVttaWxvX3JlcyRhbm5vdGF0aW9uX2luZGVwdGhfZnJhY3Rpb24gPCAwLjZdIDwtIE5BCmBgYAoKCkkgY2FuIHJlY292ZXIgYWxsIHRoZSBjbHVzdGVycyB3aGVyZSBEQSB3YXMgZGV0ZWN0ZWQgaW4gdGhlIG9yaWdpbmFsIHBhcGVyCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpncm91cC5ieSA9ICJhbm5vdGF0aW9uX2luZGVwdGgiCnBhcGVyX0RBIDwtIGxpc3QoY2lycmhvdGljPWMoIk1QcyAoNCkiLCJNUHMgKDUpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRW5kb3RoZWxpYSAoNikiLCAiRW5kb3RoZWxpYSAoNykiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZXMgKDMpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVGNlbGxzICgyKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk15b2ZpYnJvYmxhc3RzIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgaGVhbHRoeT1jKCJNUHMgKDcpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVuZG90aGVsaWEgKDEpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRjZWxscyAoMSkiLCAiVGNlbGxzICgzKSIsIlRjZWxscyAoMSkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiSUxDcyAoMSkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICApCgpleHBEQV9kZiA8LSBiaW5kX3Jvd3MoCiAgZGF0YS5mcmFtZShhbm5vdGF0aW9uX2luZGVwdGggPSBwYXBlcl9EQVtbImNpcnJob3RpYyJdXSwgcHJlZF9EQT0iY2lycmhvdGljIiksCiAgZGF0YS5mcmFtZShhbm5vdGF0aW9uX2luZGVwdGggPSBwYXBlcl9EQVtbImhlYWx0aHkiXV0sIHByZWRfREE9ImhlYWx0aHkiKQogICkKCnBsMSA8LSBtaWxvX3JlcyAlPiUKICBsZWZ0X2pvaW4oZXhwREFfZGYpICU+JQogIG11dGF0ZShpc19zaWduaWYgPSBpZmVsc2UoU3BhdGlhbEZEUiA8IDAuMSwgMSwgMCkpICU+JQogIG11dGF0ZShsb2dGQ19jb2xvciA9IGlmZWxzZShpc19zaWduaWY9PTEsIGxvZ0ZDLCBOQSkpICU+JQogIGFycmFuZ2UoYW5ub3RhdGlvbl9saW5lYWdlKSAlPiUKICBtdXRhdGUoTmhvb2Q9ZmFjdG9yKE5ob29kLCBsZXZlbHM9dW5pcXVlKE5ob29kKSkpICU+JQogIGZpbHRlcighaXMubmEoYW5ub3RhdGlvbl9saW5lYWdlKSkgJT4lCiAgZ2dwbG90KGFlcyhhbm5vdGF0aW9uX2luZGVwdGgsIGxvZ0ZDLCBjb2xvcj1sb2dGQ19jb2xvcikpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudDIoKSArCiAgZ3VpZGVzKGNvbG9yPSJub25lIikgKwogIHhsYWIoZ3JvdXAuYnkpICsgeWxhYigiTG9nIEZvbGQgQ2hhbmdlIikgKwogIGdnYmVlc3dhcm06Omdlb21fcXVhc2lyYW5kb20oYWxwaGE9MSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgZmFjZXRfZ3JpZChhbm5vdGF0aW9uX2xpbmVhZ2V+Liwgc2NhbGVzPSJmcmVlIiwgc3BhY2U9ImZyZWUiKSArCiAgdGhlbWVfYncoYmFzZV9zaXplPTIyKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gIGVsZW1lbnRfdGV4dChhbmdsZT0wKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgKQoKcGwyIDwtIG1pbG9fcmVzICU+JQogIGxlZnRfam9pbihleHBEQV9kZikgJT4lCiAgIyBkcGx5cjo6ZmlsdGVyKCFpcy5uYShwcmVkX0RBKSkgJT4lCiAgZ3JvdXBfYnkoYW5ub3RhdGlvbl9pbmRlcHRoKSAlPiUKICBzdW1tYXJpc2UocHJlZF9EQT1kcGx5cjo6Zmlyc3QocHJlZF9EQSksIGFubm90YXRpb25fbGluZWFnZT1kcGx5cjo6Zmlyc3QoYW5ub3RhdGlvbl9saW5lYWdlKSkgJT4lCiAgbXV0YXRlKGVuZD1pZmVsc2UocHJlZF9EQT09ImhlYWx0aHkiLCAwLCAxKSwKICAgICAgICAgc3RhcnQ9aWZlbHNlKHByZWRfREE9PSJoZWFsdGh5IiwgMSwgMCkpICU+JQogIGZpbHRlcighaXMubmEoYW5ub3RhdGlvbl9saW5lYWdlKSkgJT4lCiAgZ2dwbG90KGFlcyhhbm5vdGF0aW9uX2luZGVwdGgsIHN0YXJ0LCB4ZW5kID0gYW5ub3RhdGlvbl9pbmRlcHRoLCB5ZW5kID0gZW5kLCBjb2xvcj1wcmVkX0RBKSkgKwogIGdlb21fc2VnbWVudChzaXplPTEsYXJyb3c9YXJyb3cobGVuZ3RoID0gdW5pdCgwLjEsICJucGMiKSwgdHlwZT0iY2xvc2VkIikpICsKICBjb29yZF9mbGlwKCkgKwogIHhsYWIoImFubm90YXRpb24iKSArCiAgZmFjZXRfZ3JpZChhbm5vdGF0aW9uX2xpbmVhZ2V+LiwKICAgICMgYW5ub3RhdGlvbl9saW5lYWdlfiJSYW1hY2hhbmRyYW4gZXQgYWwuXG5EQSBwcmVkaWN0aW9ucyIsCiAgICAgICAgICAgICBzY2FsZXM9ImZyZWUiLCBzcGFjZT0iZnJlZSIpICsKICAjIGd1aWRlcyhjb2xvcj0ibm9uZSIpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIsIGRpcmVjdGlvbiA9IC0xLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiZW5yaWNoZWQgaW4gY2lycmhvdGljIiwgImVucmljaGVkIGluIGhlYWx0aHkiKSwKICAgICAgICAgICAgICAgICAgICAgbmEudHJhbnNsYXRlID0gRiwKICAgICAgICAgICAgICAgICAgICAgbmFtZT0iUmFtYWNoYW5kcmFuIGV0IGFsLlxuREEgcHJlZGljdGlvbnMiKSArCiAgZ3VpZGVzKGNvbG9yPWd1aWRlX2xlZ2VuZChuY29sID0gMSkpICsKICB0aGVtZV9idyhiYXNlX3NpemU9MjIpICsKICB5bGltKC0wLjEsMS4xKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCksCiAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMCwwLDAsMCksICJjbSIpLCBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmZpZzRfYmxlZnQgPC0gKHBsMiArIHBsMSArCiAgcGxvdF9sYXlvdXQod2lkdGhzPWMoMSwxMCksIGd1aWRlcyA9ICJjb2xsZWN0IikgJiB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAndG9wJywgbGVnZW5kLmp1c3RpZmljYXRpb24gPSAwKSkKCmZpZzRfYmxlZnQgKwogIGdnc2F2ZSgifi9tb3VudC9nZHJpdmUvbWlsby9GaWd1cmVzL2xpdmVyX3YyL2xpdmVyX0RBY29tcGFyaXNvbi5wZGYiLCB3aWR0aD04LCBoZWlnaHQgPSAxMykKYGBgCgojIyMgQ2xvc2UtdXAgb24gRW5kb3RoZWxpYWwgbGluZWFnZQoKYGBge3J9CmVuZG9fbWlsbyA8LSBzY2F0ZXI6OnJ1blVNQVAobGl2ZXJfbWlsb1ssbGl2ZXJfbWlsbyRhbm5vdGF0aW9uX2xpbmVhZ2U9PSJFbmRvdGhlbGlhIl0sICBkaW1yZWQ9J1BDQScpCnBsb3RVTUFQKGVuZG9fbWlsbywgY29sb3VyX2J5ID0gImFubm90YXRpb25faW5kZXB0aCIpCmBgYAoKYGBge3J9CnVtYXBfZGYgPC0gZGF0YS5mcmFtZShyZWR1Y2VkRGltKGVuZG9fbWlsbywgIlVNQVAiKSkKY29sbmFtZXModW1hcF9kZikgPC0gYygiVU1BUF8xIiwgIlVNQVBfMiIpCgplbmRvX3VtYXAgPC0gY2JpbmQodW1hcF9kZiwgY29uZGl0aW9uPWVuZG9fbWlsbyRjb25kaXRpb24pICU+JQogICBnZ3Bsb3QoYWVzKFVNQVBfMSwgVU1BUF8yLCBjb2xvcj1jb25kaXRpb24pKSArCiAgZ2VvbV9wb2ludChzaXplPTAuMywgYWxwaGE9MC41KSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDEiLCBuYW1lPScnKSArCiAgeGxhYigiVU1BUDEiKSArIHlsYWIoIlVNQVAyIikgKwogIGNvb3JkX2ZpeGVkKCkgKwogIGd1aWRlcyhjb2xvcj0ibm9uZSIpICsKICBmYWNldF93cmFwKGNvbmRpdGlvbn4uLCBuY29sPTEpICsKICB0aGVtZV9ub3RoaW5nKCkgKwogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uPWMoMC45LDAuOSksCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvcj1OQSksIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMikpCmBgYAoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpdmVyX21pbG8yIDwtIGxpdmVyX21pbG8Kc3Vic2V0Lm5ob29kcyA8LSBzdHJfZGV0ZWN0KG1pbG9fcmVzJGFubm90YXRpb25faW5kZXB0aCwgIkVuZG8iKQpyZWR1Y2VkRGltKGxpdmVyX21pbG8yLCAiVU1BUCIpW2NvbG5hbWVzKGVuZG9fbWlsbyksXSA8LSByZWR1Y2VkRGltKGVuZG9fbWlsbywgIlVNQVAiKSAKCmVuZG9fZ3IgPC0KICBwbG90Tmhvb2RHcmFwaERBKAogIGxpdmVyX21pbG8yLCBtaWxvX3JlcywKICBzdWJzZXQubmhvb2RzID0gd2hpY2gobWlsb19yZXMkYW5ub3RhdGlvbl9saW5lYWdlID09ICJFbmRvdGhlbGlhIiksIAogIHNpemVfcmFuZ2U9YygxLDQpLAogICMgKSA9KVsxOihsZW5ndGgoKS0xKV0sIAogIGFscGhhID0gMC4xCiAgKSAgKwogICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjIpKQogIAojIGxpdmVyX21pbG8yIDwtIGxpdmVyX21pbG8KIyBzdWJzZXQubmhvb2RzIDwtIHN0cl9kZXRlY3QobWlsb19yZXMkYW5ub3RhdGlvbl9pbmRlcHRoLCAiRW5kbyIpCiMgcmVkdWNlZERpbShsaXZlcl9taWxvMiwgIlVNQVAiKVtjb2xuYW1lcyhlbmRvX21pbG8pLF0gPC0gcmVkdWNlZERpbShlbmRvX21pbG8sICJVTUFQIikgCiMgZW5kb19ncl9ncm91cHMgPC0gcGxvdE5ob29kR3JvdXBzKGxpdmVyX21pbG8yLCBtaWxvX3Jlc19lbmRvZ3JvdXBzW21pbG9fcmVzX2VuZG9ncm91cHMkYW5ub3RhdGlvbl9saW5lYWdlPT0iRW5kb3RoZWxpYSIsXSwgCiMgICAgICAgICAgICAgICAgIHNob3dfZ3JvdXBzID0gYygiNTQiLCAiNzAiKSwKIyAgICAgICAgICAgICAgICAgc2l6ZV9yYW5nZT1jKDEsNCksCiMgICAgICAgICAgICAgICAgIHN1YnNldC5uaG9vZHMgPSBtaWxvX3Jlc19lbmRvZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZT09IkVuZG90aGVsaWEiKSArCiMgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiNTQiPWJyZXdlci5wYWwoNCwgIlNwZWN0cmFsIilbMl0sICI3MCI9YnJld2VyLnBhbCg0LCAiU3BlY3RyYWwiKVszXSksIAojICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIjU0Ij0iVW5pbmp1cmVkIGdyb3VwIiwgJzcwJz0gIkNpcnJob3RpYyBncm91cCIpLAojICAgICAgICAgICAgICAgICAgICAgbmEudmFsdWU9IndoaXRlIiwKIyAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiTmhvb2QgZ3JvdXAiCiMgICAgICAgICAgICAgICAgICAgICApICsKIyAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMikpCgpmaWc0X2JyaWdodDEgPC0gKChlbmRvX3VtYXAgKyBlbmRvX2dyICkgKyAKICBwbG90X2xheW91dCh3aWR0aHMgPSBjKDEsMiksIAogICAgICAgICAgICAgICAgZ3VpZGVzID0gImNvbGxlY3QiCiAgICAgICAgICAgICAgICApKSAKZmlnNF9icmlnaHQxCmBgYAoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gbmhfZ3JhcGggPC0gbmhvb2RHcmFwaChsaXZlcl9taWxvKVtzdWJzZXQubmhvb2RzLHN1YnNldC5uaG9vZHNdIC0tPgo8IS0tIG5oX2dyYXBoIDwtIGdyYXBoX2Zyb21fYWRqYWNlbmN5X21hdHJpeChuaF9ncmFwaCkgLS0+Cgo8IS0tIGNvbF92YWxzIDwtIGNvbERhdGEobGl2ZXJfbWlsbylbYXMubnVtZXJpYyh2ZXJ0ZXhfYXR0cihuaF9ncmFwaCkkbmFtZSksIGNvbG91cl9ieV0gLS0+CjwhLS0gVihuaF9ncmFwaCkkY29sb3VyX2J5IDwtIGlmZWxzZShtaWxvX3Jlc1tzdWJzZXQubmhvb2RzLCJTcGF0aWFsRkRSIl0gPiAwLjEsIDAsIG1pbG9fcmVzW3N1YnNldC5uaG9vZHMsImxvZ0ZDIl0pIC0tPgo8IS0tIGdncmFwaChzaW1wbGlmeShuaF9ncmFwaCkpICsgLS0+CjwhLS0gICAgICAgZ2VvbV9lZGdlX2xpbmswKGVkZ2VfY29sb3VyID0gImdyZXk2NiIsIGVkZ2VfYWxwaGE9MC4yKSAgICsgLS0+CjwhLS0gICAgICAgZ2VvbV9ub2RlX3BvaW50KGFlcyhmaWxsID0gY29sb3VyX2J5KSwgc2hhcGU9MjEsIHNpemU9MikgKyAtLT4KPCEtLSAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKCkgLS0+CjwhLS0gYGBgIC0tPgoKCiMjIyBDbG9zZS11cCBvbiBDaG9sYW5naW9jeXRlcwoKYGBge3J9CmNob2xfbWlsbyA8LSBzY2F0ZXI6OnJ1blVNQVAobGl2ZXJfbWlsb1ssbGl2ZXJfbWlsbyRhbm5vdGF0aW9uX2xpbmVhZ2U9PSJDaG9sYW5naW9jeXRlcyJdLCAgZGltcmVkPSdQQ0EnKQpwbG90VU1BUChjaG9sX21pbG8sIGNvbG91cl9ieSA9ICJhbm5vdGF0aW9uX2luZGVwdGgiKQoKcGxvdFVNQVAoY2hvbF9taWxvLCBjb2xvdXJfYnkgPSAicGVyY2VudC5taXRvIikKYGBgCgpGaWx0ZXIgb3V0IGNlbGxzIHRoYXQgc2hvdyBjb250YW1pbmF0aW9uIGZyb20gaW1tdW5lIGNlbGxzIChleHByZXNzaW9uIG9mIGltbXVuZSBtYXJrZXJzKQoKYGBge3J9CmtlZXAgPC0gbG9nY291bnRzKGNob2xfbWlsbylbIkNEMTkiLF0gPT0gMCB8IGxvZ2NvdW50cyhjaG9sX21pbG8pWyJNUzRBMSIsXSA9PSAwCmNob2xfbWlsbyA8LSBjaG9sX21pbG9bLGtlZXBdCmNob2xfbWlsbyA8LSBzY2F0ZXI6OnJ1blVNQVAoY2hvbF9taWxvLCAgZGltcmVkPSdQQ0EnKQoKcGxvdFVNQVAoY2hvbF9taWxvLCBjb2xvdXJfYnkgPSAiYW5ub3RhdGlvbl9pbmRlcHRoIikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTh9CnVtYXBfZGYgPC0gZGF0YS5mcmFtZShyZWR1Y2VkRGltKGNob2xfbWlsbywgIlVNQVAiKSkKY29sbmFtZXModW1hcF9kZikgPC0gYygiVU1BUF8xIiwgIlVNQVBfMiIpCgpjaG9sX3VtYXAgPC0gY2JpbmQodW1hcF9kZiwgY29uZGl0aW9uPWNob2xfbWlsbyRjb25kaXRpb24pICU+JQogICBnZ3Bsb3QoYWVzKFVNQVBfMSwgVU1BUF8yLCBjb2xvcj1jb25kaXRpb24pKSArCiAgZ2VvbV9wb2ludChzaXplPTAuMywgYWxwaGE9MC41KSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDEiLCBuYW1lPScnKSArCiAgeGxhYigiVU1BUDEiKSArIHlsYWIoIlVNQVAyIikgKwogIGNvb3JkX2ZpeGVkKCkgKwogIGd1aWRlcyhjb2xvcj0ibm9uZSIpICsKICBmYWNldF93cmFwKGNvbmRpdGlvbn4uLCBuY29sPTEpICsKICB0aGVtZV9ub3RoaW5nKCkgKwogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uPWMoMC45LDAuOSksCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvcj1OQSksIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMikpCgpjaG9sX3VtYXAKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NCwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbGl2ZXJfbWlsbzIgPC0gbGl2ZXJfbWlsbwpzdWJzZXQubmhvb2RzIDwtIG1pbG9fcmVzJGFubm90YXRpb25fbGluZWFnZT09IkNob2xhbmdpb2N5dGVzIgpyZWR1Y2VkRGltKGxpdmVyX21pbG8yLCAiVU1BUCIpW2NvbG5hbWVzKGNob2xfbWlsbyksXSA8LSByZWR1Y2VkRGltKGNob2xfbWlsbywgIlVNQVAiKSAKCmNob2xfZ3IgPC0KICBwbG90Tmhvb2RHcmFwaERBKAogIGxpdmVyX21pbG8yLCBtaWxvX3JlcywKICBzdWJzZXQubmhvb2RzID0gc3Vic2V0Lm5ob29kcywKICBzaXplX3JhbmdlPWMoMiw1KSwKICAjICkgPSlbMToobGVuZ3RoKCktMSldLCAKICBhbHBoYSA9IDAuMQogICkgICsKICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMiksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTI0KSkKICAKKGNob2xfdW1hcCArIGNob2xfZ3IgKSArIAogIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMSwyKSwgCiAgICAgICAgICAgICAgICBndWlkZXMgPSAiY29sbGVjdCIKICAgICAgICAgICAgICAgICkKIyBmaWc0X2JyaWdodDEgKwojICAgZ2dzYXZlKCJ+L21pbG9fb3V0cHV0L2xpdmVyX2VuZG9HcmFwaC5wZGYiLCB3aWR0aD05LCBoZWlnaHQgPSA1KSAgCgpgYGAKCiMjIyBEaWZmZXJlbnRpYWwgR2VuZSBFeHByZXNzaW9uIGFuYWx5c2lzCgpJbiBhIHN1YnNldCBvZiBsaW5lYWdlcywgd2Ugd2FudCB0byB0ZXN0IGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBiZXR3ZWVuIG5laWdoYm91cmhvb2RzIGVucmljaGVkIGluIGNpcnJob3RpYyBjZWxscyBhbmQgbmVpZ2hib3VyaG9vZHMgZW5yaWNoZWQKCjwhLS0gKE5vdyBjb2RlZCBpbiBgbWlsb1JcUlx0ZXN0RGlmZkV4cC5SYCkgLS0+Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSAucGVyZm9ybV9jb3VudHNfZGdlIDwtIGZ1bmN0aW9uKGV4cHJzLmRhdGEsIHRlc3QubW9kZWwsIGdlbmUub2Zmc2V0PWdlbmUub2Zmc2V0LCAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1vZGVsLmNvbnRyYXN0cz1OVUxMLCBuLmNvZWY9TlVMTCl7IC0tPgoKPCEtLSAgICAgaS5kZ2UgPC0gREdFTGlzdChjb3VudHM9ZXhwcnMuZGF0YSwgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgbGliLnNpemU9bG9nKGNvbFN1bXMoZXhwcnMuZGF0YSkpKSAtLT4KCjwhLS0gICAgIGlmKGlzVFJVRShnZW5lLm9mZnNldCkpeyAtLT4KPCEtLSAgICAgICAgIG4uZ2VuZSA8LSBhcHBseShleHBycy5kYXRhLCAyLCBmdW5jdGlvbihYKSBzdW0oWCA+IDApKSAtLT4KPCEtLSAgICAgICAgIGlmKG5jb2wodGVzdC5tb2RlbCkgPT0gMil7IC0tPgo8IS0tICAgICAgICAgICAgIHRlc3QubW9kZWwgPC0gY2JpbmQodGVzdC5tb2RlbCwgbi5nZW5lKSAtLT4KPCEtLSAgICAgICAgICAgICBjb2xuYW1lcyh0ZXN0Lm1vZGVsKSA8LSBjKGNvbG5hbWVzKHRlc3QubW9kZWwpWzE6Ml0sICJOR2VuZXMiKSAtLT4KPCEtLSAgICAgICAgIH0gZWxzZSBpZiAobmNvbCh0ZXN0Lm1vZGVsKSA+IDIpeyAtLT4KPCEtLSAgICAgICAgICAgICB0ZXN0Lm1vZGVsIDwtIGNiaW5kKHRlc3QubW9kZWxbLCAxXSwgbi5nZW5lLCB0ZXN0Lm1vZGVsWywgYygyOm5jb2wodGVzdC5tb2RlbCkpXSkgLS0+CjwhLS0gICAgICAgICAgICAgY29sbmFtZXModGVzdC5tb2RlbCkgPC0gYyhjb2xuYW1lcyh0ZXN0Lm1vZGVsKVsxXSwgIk5HZW5lcyIsIGNvbG5hbWVzKHRlc3QubW9kZWxbLCBjKDI6bmNvbCh0ZXN0Lm1vZGVsKSldKSkgLS0+CjwhLS0gICAgICAgICB9IGVsc2V7IC0tPgo8IS0tICAgICAgICAgICAgIGlmKG5jb2wodGVzdC5tb2RlbCkgPCAyKXsgLS0+CjwhLS0gICAgICAgICAgICAgICAgIHdhcm5pbmcoIk9ubHkgb25lIGNvbHVtbiBpbiBtb2RlbCBtYXRyaXggLSBtdXN0IGhhdmUgYXQgbGVhc3QgMi4gZ2VuZS5vZmZzZXQgZm9yY2VkIHRvICBGQUxTRSIpIC0tPgo8IS0tICAgICAgICAgICAgIH0gLS0+CjwhLS0gICAgICAgICB9IC0tPgo8IS0tICAgICB9IC0tPgoKPCEtLSAgICAgaS5kZ2UgPC0gZXN0aW1hdGVEaXNwKGkuZGdlLCB0ZXN0Lm1vZGVsKSAtLT4KPCEtLSAgICAgaS5maXQgPC0gZ2xtUUxGaXQoaS5kZ2UsIHRlc3QubW9kZWwsIHJvYnVzdD1UUlVFKSAtLT4KCjwhLS0gICAgIGlmKCFpcy5udWxsKG1vZGVsLmNvbnRyYXN0cykpeyAtLT4KPCEtLSAgICAgICAgIG1vZC5jb25zdHJhc3QgPC0gbWFrZUNvbnRyYXN0cyhjb250cmFzdHM9bW9kZWwuY29udHJhc3RzLCBsZXZlbHM9dGVzdC5tb2RlbCkgLS0+CjwhLS0gICAgICAgICBpLnJlcyA8LSBhcy5kYXRhLmZyYW1lKHRvcFRhZ3MoZ2xtUUxGVGVzdChpLmZpdCwgY29udHJhc3Q9bW9kLmNvbnN0cmFzdCksIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvcnQuYnk9J25vbmUnLCBuPUluZikpIC0tPgo8IS0tICAgICB9IGVsc2V7IC0tPgo8IS0tICAgICAgICAgaWYoaXMubnVsbChuLmNvZWYpKXsgLS0+CjwhLS0gICAgICAgICAgICAgbi5jb2VmIDwtIG5jb2wodGVzdC5tb2RlbCkgLS0+CjwhLS0gICAgICAgICB9IC0tPgo8IS0tICAgICAgICAgaS5yZXMgPC0gYXMuZGF0YS5mcmFtZSh0b3BUYWdzKGdsbVFMRlRlc3QoaS5maXQsIGNvZWY9bi5jb2VmKSwgc29ydC5ieT0nbm9uZScsIG49SW5mKSkgLS0+CjwhLS0gICAgIH0gLS0+CjwhLS0gICAgIHJldHVybihpLnJlcykgLS0+CjwhLS0gfSAtLT4KCjwhLS0gYGBgIC0tPgoKQWRkIG5ob29kIGV4cHJlc3Npb24gdG8gc3BlZWQtdXAgcGxvdHRpbmcgb2YgaGVhdG1hcHMKCmBgYHtyfQpsaXZlcl9taWxvIDwtIGNhbGNOaG9vZEV4cHJlc3Npb24obGl2ZXJfbWlsbywgYXNzYXkgPSAibG9nY291bnRzIiwgc3Vic2V0LnJvdyA9IGh2Z3MpCmBgYAoKCiMjIEVuZG90aGVsaWEKClJlYnV0dGFsIGZpZ3VyZSBzaG93Y2FzaW5nIGdyb3VwaW5nCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTR9CnNldC5zZWVkKDQyKQptaWxvX3Jlc19lbmRvZ3JvdXBzIDwtIGdyb3VwTmhvb2RzKGxpdmVyX21pbG8sIG1pbG9fcmVzLCBtYXgubGZjLmRlbHRhID0gMiwgb3ZlcmxhcCA9IDEpCgpwMSA8LSBwbG90Tmhvb2RHcm91cHMobGl2ZXJfbWlsbywgbWlsb19yZXNfZW5kb2dyb3VwcywgCiAgICAgICAgICAgICAgICBzaXplX3JhbmdlPWMoMSwzKSkgCgptaWxvX3Jlc19lbmRvZ3JvdXBzIDwtIGFubm90YXRlTmhvb2RzKGxpdmVyX21pbG8sIG1pbG9fcmVzX2VuZG9ncm91cHMsICdhbm5vdGF0aW9uX2xpbmVhZ2UnKQoKcDIgPC0gcGxvdERBYmVlc3dhcm0obWlsb19yZXNfZW5kb2dyb3VwcywgZ3JvdXAuYnkgPSAnTmhvb2RHcm91cCcpICsKICBmYWNldF9ncmlkKGFubm90YXRpb25fbGluZWFnZX4uLCBzY2FsZXM9ImZyZWUiLCBzcGFjZT0iZnJlZSIpCgoKIyMgUGxvdCBleHByZXNzaW9uIGluIFQgY2VsbCBuZWlnaGJvdXJob29kcwptYXJrZXJzX2RmIDwtIHJlYWRfY3N2KCJ+L21vdW50L2dkcml2ZS9taWxvL1NUYWJsZTNfUmFtYWNoYW5kcmFuLmNzdiIpCnRjZWxsX21hcmtlcl9nZW5lcyA8LSAKICBtYXJrZXJzX2RmICU+JQogIGZpbHRlcihjbHVzdGVyICVpbiUgYygiVGNlbGwiLCAiSUxDIikpICU+JQogIHRvcF9uKDMwLCBteUFVQykgJT4lCiAgcHVsbChnZW5lKQoKcDMgPC0gcGxvdE5ob29kRXhwcmVzc2lvbkdyb3VwcyhsaXZlcl9taWxvLCBtaWxvX3Jlc19lbmRvZ3JvdXBzLCBmZWF0dXJlcyA9IHVuaXF1ZSh0Y2VsbF9tYXJrZXJfZ2VuZXMpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQubmhvb2RzID0gbWlsb19yZXNfZW5kb2dyb3VwcyROaG9vZEdyb3VwICVpbiUgYygiMyIsIjEwIiwgIjE0IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGU9VFJVRSwgY2x1c3Rlcl9mZWF0dXJlcyA9IFRSVUUsc2hvd19yb3duYW1lcyA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICB0aGVtZShzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTApKQoKYGBgCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9MTB9CigoKHAxICsgdGhlbWUoKSkvIChwMyArIHRoZW1lKHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCwgYW5nbGU9NDUpKSkpICsgCiAgcGxvdF9sYXlvdXQoaGVpZ2h0cyA9IGMoMS4xLDEpLCBndWlkZXM9ImNvbGxlY3QiCiAgICAgICAgICAgICAgKXwgCiAgKAogICAgcDIgKyB0aGVtZV9idyhiYXNlX3NpemU9MTYpICsgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTApKQogICAgKSkgKyAKICBwbG90X2xheW91dCh3aWR0aHMgPSBjKDEuNCwgMSkpICsKICBwbG90X2Fubm90YXRpb24odGFnX2xldmVscyA9IGMoIkEiLCAiQyIsICJCIikgKSArCiAgZ2dzYXZlKCJ+L21vdW50L2dkcml2ZS9taWxvL0ZpZ3VyZXMvbGl2ZXJfdjIvUkZpZ19ncm91cGluZy5wZGYiLCB3aWR0aD0xNSwgaGVpZ2h0ID0gMTIpICsKICBnZ3NhdmUoIn4vbW91bnQvZ2RyaXZlL21pbG8vRmlndXJlcy9saXZlcl92Mi9SRmlnX2dyb3VwaW5nLnBuZyIsIHdpZHRoPTE1LCBoZWlnaHQgPSAxMikKYGBgCgpHcm91cCBlbmRvdGhlbGlhbCBjZWxscyBieSBsb2dGQyBhbmQgREEgcmVzdWx0cwoKYGBge3J9Cm1pbG9fcmVzX2VuZG9ncm91cHMkYW5ub3RhdGlvbl9pbmRlcHRoW21pbG9fcmVzX2VuZG9ncm91cHMkYW5ub3RhdGlvbl9pbmRlcHRoX2ZyYWN0aW9uIDwgMC42XSA8LSBOQQptaWxvX3Jlc19lbmRvZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZVttaWxvX3Jlc19lbmRvZ3JvdXBzJGFubm90YXRpb25faW5kZXB0aF9mcmFjdGlvbiA8IDAuNl0gPC0gTkEKCiMjIEdyb3VwIG5laWdoYm91cmhvb2RzIGJ5IERBIG91dGNvbWUKbWlsb19yZXNfZW5kb2dyb3VwcyROaG9vZEdyb3VwIDwtIE5BCm1pbG9fcmVzX2VuZG9ncm91cHMkTmhvb2RHcm91cCA8LSBpZmVsc2UoKG1pbG9fcmVzX2VuZG9ncm91cHMkYW5ub3RhdGlvbl9saW5lYWdlID09ICJFbmRvdGhlbGlhIikgJiAobWlsb19yZXNfZW5kb2dyb3VwcyRTcGF0aWFsRkRSIDwgMC4xKSAmIChtaWxvX3Jlc19lbmRvZ3JvdXBzJGxvZ0ZDIDwgLTIuNSksICI1NCIsIG1pbG9fcmVzX2VuZG9ncm91cHMkTmhvb2RHcm91cCkKbWlsb19yZXNfZW5kb2dyb3VwcyROaG9vZEdyb3VwIDwtIGlmZWxzZSgobWlsb19yZXNfZW5kb2dyb3VwcyRhbm5vdGF0aW9uX2xpbmVhZ2UgPT0gIkVuZG90aGVsaWEiKSAmIChtaWxvX3Jlc19lbmRvZ3JvdXBzJFNwYXRpYWxGRFIgPCAwLjEpICYgKG1pbG9fcmVzX2VuZG9ncm91cHMkbG9nRkMgPiAyLjUpLCAiNzAiLCBtaWxvX3Jlc19lbmRvZ3JvdXBzJE5ob29kR3JvdXApCgoKbGl2ZXJfbWlsbzIgPC0gbGl2ZXJfbWlsbwpzdWJzZXQubmhvb2RzIDwtIHN0cl9kZXRlY3QobWlsb19yZXMkYW5ub3RhdGlvbl9pbmRlcHRoLCAiRW5kbyIpCnJlZHVjZWREaW0obGl2ZXJfbWlsbzIsICJVTUFQIilbY29sbmFtZXMoZW5kb19taWxvKSxdIDwtIHJlZHVjZWREaW0oZW5kb19taWxvLCAiVU1BUCIpIAplbmRvX2dyX2dyb3VwcyA8LSBwbG90Tmhvb2RHcm91cHMobGl2ZXJfbWlsbzIsIG1pbG9fcmVzX2VuZG9ncm91cHNbbWlsb19yZXNfZW5kb2dyb3VwcyRhbm5vdGF0aW9uX2xpbmVhZ2U9PSJFbmRvdGhlbGlhIixdLCAKICAgICAgICAgICAgICAgIHNob3dfZ3JvdXBzID0gYygiNTQiLCAiNzAiKSwKICAgICAgICAgICAgICAgIHNpemVfcmFuZ2U9YygxLDQpLAogICAgICAgICAgICAgICAgc3Vic2V0Lm5ob29kcyA9IG1pbG9fcmVzX2VuZG9ncm91cHMkYW5ub3RhdGlvbl9saW5lYWdlPT0iRW5kb3RoZWxpYSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiNTQiPWJyZXdlci5wYWwoNCwgIlNwZWN0cmFsIilbMl0sICI3MCI9YnJld2VyLnBhbCg0LCAiU3BlY3RyYWwiKVszXSksIAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCI1NCI9IlVuaW5qdXJlZCBncm91cCIsICc3MCc9ICJDaXJyaG90aWMgZ3JvdXAiKSwKICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZT0id2hpdGUiLAogICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiTmhvb2QgZ3JvdXAiCiAgICAgICAgICAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTE4LCBmaWcuaGVpZ2h0PTV9CmZpZzRfYnJpZ2h0MSA8LSAoKGVuZG9fdW1hcCArIGVuZG9fZ3IpICsgCiAgcGxvdF9sYXlvdXQod2lkdGhzID0gYygxLDIpLCBndWlkZXM9ImNvbGxlY3QiCiAgICAgICAgICAgICAgICApKSAmCiAgdGhlbWUobGVnZW5kLmJveCA9ICJob3Jpem9udGFsIiwgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsIGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiKQpmaWc0X2JyaWdodDEKYGBgCgoKQ2FsY3VsYXRlIG1hcmtlciBnZW5lcyBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzCmBgYHtyfQptaXRvX2dlbmVzIDwtIHN0cl9kZXRlY3QoaHZncywgIl5NVC0iKQptYXJrZXJzX2RmIDwtIGZpbmROaG9vZEdyb3VwTWFya2VycyhsaXZlcl9taWxvLCBkYS5yZXMgPSBtaWxvX3Jlc19lbmRvZ3JvdXBzLCBhc3NheT0iY291bnRzIiwKICAgICAgICAgICAgICAgICAgICAgIHN1YnNldC5uaG9vZHMgPSAobWlsb19yZXNfZW5kb2dyb3VwcyROaG9vZEdyb3VwICVpbiUgYygiNTQiLCAiNzAiKSksCiAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQuZ3JvdXBzID0gYygiNTQiLCAiNzAiKSwKICAgICAgICAgICAgICAgICAgICAgIHN1YnNldC5yb3cgPSBodmdzWyFtaXRvX2dlbmVzXSwKICAgICAgICAgICAgICAgICAgICAgIGFnZ3JlZ2F0ZS5zYW1wbGVzID0gVFJVRSwgc2FtcGxlX2NvbCA9ICJkYXRhc2V0IgogICAgICAgICAgICAgICAgICAgICAgKQoKbWlsb19yZXNfZW5kb2dyb3Vwc1ttaWxvX3Jlc19lbmRvZ3JvdXBzJE5ob29kR3JvdXAgJWluJSBjKCI1NCIsICI3MCIpLF0KCmNvbG5hbWVzKG1hcmtlcnNfZGYpIDwtIHN0cl9yZXBsYWNlKGNvbG5hbWVzKG1hcmtlcnNfZGYpLCAiNzAiLCAiY2lyciIpCmNvbG5hbWVzKG1hcmtlcnNfZGYpIDwtIHN0cl9yZXBsYWNlKGNvbG5hbWVzKG1hcmtlcnNfZGYpLCAiNTQiLCAidW5pbmoiKQpgYGAKCiMjIyMgVmlzdWFsaXplIGFzIHZvbGNhbm8gCgpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgpoaWdobGlnaHRfZ2VuZXMgPC0gYygiUExWQVAiLCAiVldBMSIsICJBQ0tSMSIsICJJTDMyIiwKICAgICAgICAgICAgICAgICAgICAgIkNMRUM0RyIsICJDTEVDNE0iLCAiRkNOMiIsICJGQ04zIiwKICAgICAgICAgICAgICAgICAgICAgIkxFRjEiKQoKbWFya2VyLmRmIDwtIG1hcmtlcnNfZGYKbWFya2VyLmRmICU+JQogIG11dGF0ZShsYWJlbD1pZmVsc2UoR2VuZUlEICVpbiUgaGlnaGxpZ2h0X2dlbmVzLCBHZW5lSUQsIE5BKSkgJT4lCiAgZ2dwbG90KGFlcyhsb2dGQ19jaXJyLCAtbG9nMTAoYWRqLlAuVmFsX2NpcnIpLCAKICAgICAgICAgICAgICMgY29sb3I9aGlnaGxpZ2h0CiAgICAgICAgICAgICApKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1sYWJlbCksIGNvbG9yPSJyZWQiKSArCiAgeGxhYigibG9nRkMiKSArIHlsYWIoIi0gbG9nMTAoQWRqLiBQIHZhbHVlKSIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAyMikKICAKYGBgCgoKIyMjIyBWaXN1YWxpemUgYXMgaGVhdG1hcCAKKGdlbmUgZXhwcmVzc2lvbiB2YWx1ZXMgYXJlIHNjYWxlZCBiZXR3ZWVuIDAgYW5kIDEgZm9yIGVhY2ggZ2VuZSkKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTIsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm1hcmtlcl9nZW5lcyA8LSBtYXJrZXIuZGYgJT4lCiAgZHBseXI6OmZpbHRlcihhZGouUC5WYWxfY2lyciA8IDAuMDUpICU+JQogIHB1bGwoR2VuZUlEKQoKZmlnNF9iYnJpZ2h0IDwtCiAgcGxvdE5ob29kRXhwcmVzc2lvbkRBKGxpdmVyX21pbG8sIG1pbG9fcmVzX2VuZG9ncm91cHMsIGMobWFya2VyX2dlbmVzKSwgY2x1c3Rlcl9mZWF0dXJlcyA9IFRSVUUsIGFzc2F5ID0gImNvdW50cyIsCiAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMSwKICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX3RvXzEgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgc3Vic2V0Lm5ob29kcyA9ICBtaWxvX3Jlc19lbmRvZ3JvdXBzJE5ob29kR3JvdXAgJWluJSBjKCI1NCIsICI3MCIpLAogICAgICAgICAgICAgICAgICAgICAgIyBncmlkLnNwYWNlID0gImZyZWUiLAogICAgICAgICAgICAgICAgICAgICAgaGlnaGxpZ2h0X2ZlYXR1cmVzID0gaGlnaGxpZ2h0X2dlbmVzLCBzaG93X3Jvd25hbWVzID0gRkFMU0UKICAgICAgICAgICAgICAgICAgICAgICkgKwogIHlsYWIoIkRFIGdlbmVzIikrCiAgIyBmYWNldF9ncmlkKC5+Tmhvb2RHcm91cCwgc2NhbGVzPSJmcmVlIiwgc3BhY2U9ImZyZWUiKQogICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjQpKSArCiAgcGxvdF9sYXlvdXQoaGVpZ2h0cyA9IGMoMSwxMCkpICYgdGhlbWUobGVnZW5kLm1hcmdpbiA9IG1hcmdpbigwLDAsMCw2MCksIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQoKICAKcGwzIDwtIGZpZzRfYmJyaWdodCRkYXRhICU+JQogIGdncGxvdChhZXMobG9nRkNfcmFuaywgMSxmaWxsPWxvZ0ZDKSkgKwogIGdlb21fdGlsZSgpICsKICAgICAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemU9MTYpICsKICAgIHlsYWIoIiIpICsKICBzY2FsZV9maWxsX2dyYWRpZW50MihuYW1lPSJEQSBsb2dGQyIpICsKICAgICMgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIjU0Ij1icmV3ZXIucGFsKDQsICJTcGVjdHJhbCIpWzJdLCAiNzAiPWJyZXdlci5wYWwoNCwgIlNwZWN0cmFsIilbM10pLCAKICAgICMgICAgICAgICAgICAgICAgIGxhYmVscz1jKCI1NCI9IlVuaW5qdXJlZCBncm91cCIsICc3MCc9ICJDaXJyaG90aWMgZ3JvdXAiKSwKICAgICMgICAgICAgICAgICAgICAgIG5hLnZhbHVlPSJ3aGl0ZSIsCiAgICAjICAgICAgICAgICAgICAgICBuYW1lID0gIk5ob29kIGdyb3VwIgogICAgIyAgICAgICAgICAgICAgICAgKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLjAxLCAwKSkgKwogICAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksIAogICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKCmZpZzRfYmJyaWdodCA8LSBwbDMgLyBmaWc0X2JicmlnaHQgICsKICBwbG90X2xheW91dChoZWlnaHRzID0gYygxLDIwKSkKCmZpZzRfYmJyaWdodApgYGAKCiMjIyBHTyB0ZXJtIGFuYWx5c2lzCgpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyBCaW9jTWFuYWdlcjo6aW5zdGFsbCgnY2x1c3RlclByb2ZpbGVyJykKIyBCaW9jTWFuYWdlcjo6aW5zdGFsbCgnbXNpZ2RicicpCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG1zaWdkYnIpCgptX2RmIDwtIG1zaWdkYnIoc3BlY2llcyA9ICJIb21vIHNhcGllbnMiKQptX3QyZyA8LSBtc2lnZGJyKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIiwgY2F0ZWdvcnkgPSAiQzUiLCBzdWJjYXRlZ29yeSA9ICJCUCIpICAlPiUgCiAgZHBseXI6OnNlbGVjdChnc19uYW1lLCBnZW5lX3N5bWJvbCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbWFya2VyX2dlbmVzX3VwIDwtIG1hcmtlci5kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKGFkai5QLlZhbF9jaXJyIDwgMC4wNSAmIGxvZ0ZDX2NpcnIgPiAwLjUpICU+JQogIHB1bGwoR2VuZUlEKSAKCm1hcmtlcl9nZW5lc19kb3duIDwtIG1hcmtlci5kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKGFkai5QLlZhbF9jaXJyIDwgMC4wNSAmIGxvZ0ZDX3VuaW5qID4gMC41KSAlPiUKICBwdWxsKEdlbmVJRCkKCmVtX3VwIDwtIGVucmljaGVyKG1hcmtlcl9nZW5lc191cCwgVEVSTTJHRU5FPW1fdDJnLCBwQWRqdXN0TWV0aG9kID0gImZkciIsIAogICAgICAgICAgICAgICAgICB1bml2ZXJzZSA9IGh2Z3MKICAgICAgICAgICAgICAgICAgKQplbV9kb3duIDwtIGVucmljaGVyKG1hcmtlcl9nZW5lc19kb3duLCBURVJNMkdFTkU9bV90MmcsIHBBZGp1c3RNZXRob2QgPSAiZmRyIiwgCiAgICAgICAgICAgICAgICAgICAgdW5pdmVyc2UgPSByb3duYW1lcyhsaXZlcl9taWxvKQogICAgICAgICAgICAgICAgICAgICkKCmVtX3Jlc191cCA8LSBlbV91cEByZXN1bHRbZW1fdXBAcmVzdWx0JHF2YWx1ZSA8IDAuMSxdICU+JQogIGRwbHlyOjpzZWxlY3QoLSBjKERlc2NyaXB0aW9uKSkKZW1fcmVzX2Rvd24gPC0gZW1fZG93bkByZXN1bHRbZW1fZG93bkByZXN1bHQkcXZhbHVlIDwgMC4xLF0gJT4lCiAgZHBseXI6OnNlbGVjdCgtIGMoRGVzY3JpcHRpb24pKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZ29fZW5kb191cCA8LSBlbV9yZXNfdXAgJT4lCiAgdG9wX24oMzAsIC1sb2cxMChxdmFsdWUpKSAlPiUKICAgbXV0YXRlKElEPWlmZWxzZShJRD09J0dPX0FOVElHRU5fUFJPQ0VTU0lOR19BTkRfUFJFU0VOVEFUSU9OX09GX1BFUFRJREVfT1JfUE9MWVNBQ0NIQVJJREVfQU5USUdFTl9WSUFfTUhDX0NMQVNTX0lJJywgIkdPX0FOVElHRU5fUFJFU0VOVEFUSU9OX1ZJQV9NSENfQ0xBU1NfSUkiLCBJRCkpICU+JQogIG11dGF0ZShUZXJtPWZhY3RvcihJRCwgbGV2ZWxzPXJldih1bmlxdWUoSUQpKSkpICU+JQogIGdncGxvdChhZXMoVGVybSwgLWxvZzEwKHF2YWx1ZSkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBjb29yZF9mbGlwKCkgKwogIHhsYWIoIkdPIEJpb2xvZ2ljYWwgRnVuY3Rpb24iKSArIHlsYWIoIi1sb2cxMChBZGouIHAtdmFsdWUpIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xOCkgKwogIGdndGl0bGUoIkNpcnJob3RpYyBlbmRvdGhlbGlhIikKCmdvX2VuZG9fZG93biA8LSBlbV9yZXNfZG93biAlPiUKICB0b3BfbigzMCwgLWxvZzEwKHF2YWx1ZSkpICU+JQogIG11dGF0ZShJRD1pZmVsc2UoSUQ9PSdHT19BTlRJR0VOX1BST0NFU1NJTkdfQU5EX1BSRVNFTlRBVElPTl9PRl9QRVBUSURFX09SX1BPTFlTQUNDSEFSSURFX0FOVElHRU5fVklBX01IQ19DTEFTU19JSScsICJHT19BTlRJR0VOX1BSRVNFTlRBVElPTl9WSUFfTUhDX0NMQVNTX0lJIiwgSUQpKSAlPiUKICBtdXRhdGUoVGVybT1mYWN0b3IoSUQsIGxldmVscz1yZXYodW5pcXVlKElEKSkpKSAlPiUKICBnZ3Bsb3QoYWVzKFRlcm0sIC1sb2cxMChxdmFsdWUpKSkgKwogIGdlb21fcG9pbnQoKSArCiAgY29vcmRfZmxpcCgpICsKICB4bGFiKCJHTyBCaW9sb2dpY2FsIEZ1bmN0aW9uIikgKyB5bGFiKCItbG9nMTAoQWRqLiBwLXZhbHVlKSIpICsKICB0aGVtZV9idyhiYXNlX3NpemU9MTgpICsKICBnZ3RpdGxlKCJVbmluanVyZWQgZW5kb3RoZWxpYSIpCgpnb19lbmRvX3VwCmdvX2VuZG9fZG93bgpgYGAKCgpgYGB7cn0KZW1fcmVzX3VwCmVtX3Jlc19kb3duCmBgYAoKIyMgQ2hvbGFuZ2lvY3l0ZXMKCmBgYHtyfQpzZXQuc2VlZCg0MikKbWlsb19yZXNfY2hvbGdyb3VwcyA8LSBncm91cE5ob29kcyhsaXZlcl9taWxvLCBtaWxvX3JlcywgbWF4LmxmYy5kZWx0YSA9IDAuNSwgb3ZlcmxhcCA9IDEpCgojIyBHcm91cCBuZWlnaGJvdXJob29kcyBieSBEQSBvdXRjb21lCm1pbG9fcmVzX2Nob2xncm91cHMkTmhvb2RHcm91cCA8LSBOQQptaWxvX3Jlc19jaG9sZ3JvdXBzJE5ob29kR3JvdXAgPC0gaWZlbHNlKChtaWxvX3Jlc19jaG9sZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZSA9PSAiQ2hvbGFuZ2lvY3l0ZXMiKSAmIChtaWxvX3Jlc19jaG9sZ3JvdXBzJFNwYXRpYWxGRFIgPCAwLjEpICYgKG1pbG9fcmVzX2Nob2xncm91cHMkbG9nRkMgPCAtMi41KSwgIjM4IiwgbWlsb19yZXNfY2hvbGdyb3VwcyROaG9vZEdyb3VwKQptaWxvX3Jlc19jaG9sZ3JvdXBzJE5ob29kR3JvdXAgPC0gaWZlbHNlKChtaWxvX3Jlc19jaG9sZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZSA9PSAiQ2hvbGFuZ2lvY3l0ZXMiKSAmIChtaWxvX3Jlc19jaG9sZ3JvdXBzJFNwYXRpYWxGRFIgPCAwLjEpICYgKG1pbG9fcmVzX2Nob2xncm91cHMkbG9nRkMgPiAyLjUpLCAiNDkiLCBtaWxvX3Jlc19jaG9sZ3JvdXBzJE5ob29kR3JvdXApCgpsaXZlcl9taWxvMiA8LSBsaXZlcl9taWxvCnN1YnNldC5uaG9vZHMgPC0gc3RyX2RldGVjdChtaWxvX3JlcyRhbm5vdGF0aW9uX2luZGVwdGgsICJDaG9sIikKcmVkdWNlZERpbShsaXZlcl9taWxvMiwgIlVNQVAiKVtjb2xuYW1lcyhjaG9sX21pbG8pLF0gPC0gcmVkdWNlZERpbShjaG9sX21pbG8sICJVTUFQIikgCnBsb3ROaG9vZEdyb3VwcyhsaXZlcl9taWxvMiwgbWlsb19yZXNfY2hvbGdyb3Vwc1ttaWxvX3Jlc19jaG9sZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZT09IkNob2xhbmdpb2N5dGVzIixdLCAKICAgICAgICAgICAgICAgIHNob3dfZ3JvdXBzID0gYygiNDkiLCIzOCIpLAogICAgICAgICAgICAgICAgc3Vic2V0Lm5ob29kcyA9ICBtaWxvX3Jlc19jaG9sZ3JvdXBzJGFubm90YXRpb25fbGluZWFnZSA9PSJDaG9sYW5naW9jeXRlcyIpCgpgYGAKCkNhbGN1bGF0ZSBtYXJrZXIgZ2VuZXMgYmV0d2VlbiB0aGUgdHdvIGdyb3VwcwpgYGB7cn0KIyMgRmlsdGVyIGdlbmVzIGV4cHJlc3NlZCBpbiBjaG9sYW5naW9jeXRlcwojIGNob2xfaHZncyA8LSBodmdzWyhjb3VudHMoY2hvbF9taWxvKVtodmdzLF0gPiAwKSAlPiUge3Jvd1N1bXMoLikvbmNvbChjaG9sX21pbG8pfSA+IDAuMDFdCm1pdG9fZ2VuZXMgPC0gc3RyX2RldGVjdChodmdzLCAiXk1ULSIpCgptYXJrZXJzX2RmIDwtIGZpbmROaG9vZEdyb3VwTWFya2VycyhsaXZlcl9taWxvLCBkYS5yZXMgPSBtaWxvX3Jlc19jaG9sZ3JvdXBzLCBhc3NheT0iY291bnRzIiwKICAgICAgICAgICAgICAgICAgICAgIHN1YnNldC5uaG9vZHMgPSBtaWxvX3Jlc19jaG9sZ3JvdXBzJE5ob29kR3JvdXAgJWluJWMoIjQ5IiwiMzgiKSwKICAgICAgICAgICAgICAgICAgICAgIHN1YnNldC5ncm91cHMgPSBjKCI0OSIsIjM4IiksCiAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQucm93ID0gaHZnc1shbWl0b19nZW5lc10sCiAgICAgICAgICAgICAgICAgICAgICBhZ2dyZWdhdGUuc2FtcGxlcyA9IFRSVUUsIHNhbXBsZV9jb2wgPSAiZGF0YXNldCIKICAgICAgICAgICAgICAgICAgICAgICkKCm1hcmtlcnNfZGYgCgptaWxvX3Jlc19jaG9sZ3JvdXBzW21pbG9fcmVzX2Nob2xncm91cHMkTmhvb2RHcm91cCAlaW4lYygiNDkiLCIzOCIpLF0KYGBgCgojIyMjIFZpc3VhbGl6ZSBhcyB2b2xjYW5vIAoKYGBge3IsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQptYXJrZXIuZGYuY2hvbCA8LSBtYXJrZXJzX2RmCgp2b2xjYW5vX2Nob2wgPC0KICBtYXJrZXIuZGYuY2hvbCAlPiUKICBtdXRhdGUodXA9aWZlbHNlKGxvZ0ZDXzQ5ID4gMCwgInVwIiwgImRvd24iKSkgJT4lCiAgZ3JvdXBfYnkodXApICU+JQogIG11dGF0ZShsYWJlbD1pZmVsc2UocmFuayhhZGouUC5WYWxfNDkpIDwgMTUsIEdlbmVJRCwgTkEpKSAlPiUKICAjIG11dGF0ZShsYWJlbD1pZmVsc2UoKGFkai5QLlZhbF80OSA8IDAuMDUgJiBsb2dGQ180OSA8IC0zKSB8IChhZGouUC5WYWxfNDkgPCAwLjA1ICYgbG9nRkNfNDkgPiAwKSwgR2VuZUlELCBOQSkpICU+JQogIGdncGxvdChhZXMobG9nRkNfNDksIC1sb2cxMChhZGouUC5WYWxfNDkpLCAKICAgICAgICAgICAgICMgY29sb3I9aGlnaGxpZ2h0CiAgICAgICAgICAgICApKSArIAogIGdlb21fcG9pbnQoc2l6ZT0wLjgsIGFscGhhPTAuNikgKwogIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9bGFiZWwpLCBzZWdtZW50LmFscGhhID0gMC4yKSArCiAgeGxhYigibG9nRkMiKSArIHlsYWIoIi0gbG9nMTAoQWRqLiBQIHZhbHVlKSIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAyMikKCnZvbGNhbm9fY2hvbCAgCiAgCmBgYAoKCgojIyMgR08gdGVybSBhbmFseXNpcwoKYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cm1hcmtlcl9nZW5lc19jaG9sIDwtIG1hcmtlci5kZi5jaG9sICU+JQogIGRwbHlyOjpmaWx0ZXIoYWRqLlAuVmFsXzQ5IDwgMC4wNSAmIGxvZ0ZDXzQ5ID4gMCkgJT4lCiAgcHVsbChHZW5lSUQpCgplbV91cF9jaG9sIDwtIGVucmljaGVyKG1hcmtlcl9nZW5lc19jaG9sLCBURVJNMkdFTkU9bV90MmcsIHBBZGp1c3RNZXRob2QgPSAiZmRyIiwgCiAgICAgICAgICAgICAgICAgIHVuaXZlcnNlID0gcm93bmFtZXMobGl2ZXJfbWlsbykKICAgICAgICAgICAgICAgICAgKQoKZW1fcmVzX3VwX2Nob2wgPC0gZW1fdXBfY2hvbEByZXN1bHRbZW1fdXBfY2hvbEByZXN1bHQkcXZhbHVlIDwgMC4xLF0gJT4lCiAgZHBseXI6OnNlbGVjdCgtIGMoRGVzY3JpcHRpb24pKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xNSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZ29fY2hvbF91cCA8LSBlbV9yZXNfdXBfY2hvbCAlPiUKICB0b3BfbigyMCwgLWxvZzEwKHF2YWx1ZSkpICU+JQogIG11dGF0ZShUZXJtPWZhY3RvcihJRCwgbGV2ZWxzPXJldih1bmlxdWUoSUQpKSkpICU+JQogIGdncGxvdChhZXMoVGVybSwgLWxvZzEwKHF2YWx1ZSkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBjb29yZF9mbGlwKCkgKwogIHhsYWIoIkdPIEJpb2xvZ2ljYWwgRnVuY3Rpb24iKSArIHlsYWIoIi1sb2cxMChBZGouIHAtdmFsdWUpIikgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZT0xOCkgKwogIGdndGl0bGUoIkNpcnJob3RpYyBjaG9sYW5naW9jeXRlcyIpCgpnb19jaG9sX3VwCmBgYAoKYGBge3J9CmVtX3Jlc191cF9jaG9sCmBgYApgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbWFya2VyX2dlbmVzX2Nob2xfZG93biA8LSBtYXJrZXIuZGYuY2hvbCAlPiUKICBkcGx5cjo6ZmlsdGVyKGFkai5QLlZhbF80OSA8IDAuMDUgJiBsb2dGQ180OSA8IDApICU+JQogIHB1bGwoR2VuZUlEKQoKZW1fZG93bl9jaG9sIDwtIGVucmljaGVyKG1hcmtlcl9nZW5lc19jaG9sX2Rvd24sIFRFUk0yR0VORT1tX3QyZywgcEFkanVzdE1ldGhvZCA9ICJmZHIiLCAKICAgICAgICAgICAgICAgICAgdW5pdmVyc2UgPSByb3duYW1lcyhsaXZlcl9taWxvKQogICAgICAgICAgICAgICAgICApCgplbV9yZXNfZG93bl9jaG9sIDwtIGVtX2Rvd25fY2hvbEByZXN1bHRbZW1fZG93bl9jaG9sQHJlc3VsdCRxdmFsdWUgPCAwLjEsXSAlPiUKICBkcGx5cjo6c2VsZWN0KC0gYyhEZXNjcmlwdGlvbikpCmBgYAoKCi0tLQoKQXNzZW1ibGUgZmlndXJlCmBgYHtyLCBmaWcuaGVpZ2h0PTI1LCBmaWcud2lkdGg9MTl9CmZpZzRfYm90dG9tIDwtICgoZmlnNF9ibGVmdCArIHBsb3RfbGF5b3V0KCkpIHwKICAgICAgKChmaWc0X2JyaWdodDEgKyBwbG90X2xheW91dCh0YWdfbGV2ZWwgPSAna2VlcCcpKSAvIChmaWc0X2JicmlnaHQgKyBwbG90X2xheW91dCgpKSkgKwogICAgICBwbG90X2xheW91dChoZWlnaHRzID0gYygxLDEuNikpCiAgICkgKwogIHBsb3RfbGF5b3V0KHdpZHRocz1jKDEsMS40KSkKCihmaWc0X3RvcCAvIGZpZzRfYm90dG9tKSArCiAgcGxvdF9sYXlvdXQoaGVpZ2h0cz1jKDEsMS44KSkgICsKICBnZ3NhdmUoIn4vbW91bnQvZ2RyaXZlL21pbG8vRmlndXJlcy9saXZlcl92Mi9maWc0X3Jhdy5wZGYiLCBoZWlnaHQgPSAyNiwgd2lkdGggPSAyNCwgdXNlRGluZ2JhdHM9RkFMU0UpIAogICMgZ2dzYXZlKCJ+L21vdW50L2dkcml2ZS9taWxvL0ZpZ3VyZXMvbGl2ZXJfdjIvZmlnNF9yYXcucG5nIiwgaGVpZ2h0ID0gMjYsIHdpZHRoID0gMjQsIHVzZURpbmdiYXRzPUZBTFNFKQogICMgZ2dzYXZlKCJ+L21pbG8vbXMvZmlndXJlcy9maWdzL2ZpZ3VyZTUucGRmIiwgaGVpZ2h0ID0gMjYsIHdpZHRoID0gMjIsIHVzZURpbmdiYXRzPUZBTFNFKQpgYGAKCkFzc2VtYmxlIHN1cHBsZW1lbnRhcnkgZmlndXJlCgpgYGB7ciwgZmlnLndpZHRoPTI1LCBmaWcuaGVpZ2h0PTd9CnAxIDwtIHBsb3RfZ3JpZCggZ29fZW5kb191cCsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpKSwgCiAgICAgICAgICAgICAgICAgZ29fZW5kb19kb3duKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxKSksIAogICAgICAgICAgICAgICAgIGxhYmVsX3NpemUgPSAxOCwKICAgICAgICAgICAgICAgICBuY29sPTEsCiAgICAgICAgICAgICAgICAgcmVsX2hlaWdodHMgPSBjKDIsMiksCiAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJBIiwgIkIiLCJDIikpCgpwMQoKY2hvbF9lbWIgPC0gKGNob2xfdW1hcCArIGNob2xfZ3IgKSArIAogIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMSwyKSwgCiAgICAgICAgICAgICAgICBndWlkZXMgPSAiY29sbGVjdCIKICAgICAgICAgICAgICAgICkKCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD04fQpwbG90X2dyaWQoCiAgZ29fZW5kb191cCsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpKSwgCiAgICAgICAgICAgICAgICAgZ29fZW5kb19kb3duKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoaGp1c3QgPSAxKSksIAogICAgICAgICAgICAgICAgIGxhYmVsX3NpemUgPSAxOCwKICAgICAgICAgICAgICAgICBuY29sPTEsCiAgICAgICAgICAgICAgICAgcmVsX2hlaWdodHMgPSBjKDIsMiksIHJlbF93aWR0aHMgPSBjKDIsMiksCiAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJBIiwgIkIiKQogICkgKwogIGdnc2F2ZSgifi9tb3VudC9nZHJpdmUvbWlsby9GaWd1cmVzL2xpdmVyX3YyL3N1cHBsX2ZpZ19lbmRvLnBkZiIsIGhlaWdodCA9IDEyLCB3aWR0aD0xMikgKwogIGdnc2F2ZSgifi9tb3VudC9nZHJpdmUvbWlsby9GaWd1cmVzL2xpdmVyX3YyL3N1cHBsX2ZpZ19lbmRvLnBuZyIsIGhlaWdodCA9IDEyLCB3aWR0aD0xMikKCgpgYGAKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD04fQpwbG90X2dyaWQocGxvdF9ncmlkKGNob2xfdW1hcCwgY2hvbF9nciwgdm9sY2Fub19jaG9sLCBucm93PTEscmVsX3dpZHRocyA9IGMoMSwyLDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsX3NpemUgPSAxOCwKICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkEiLCJCIiwiQyIpKSwKICAgICAgICAgICAgICAgIGdvX2Nob2xfdXAgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMSkpLCAKICAgICAgICAgICAgICAgIG5jb2w9MSwKICAgICAgICAgICAgICAgIHJlbF9oZWlnaHRzID0gYygxLDEpLAogICAgICAgICAgICAgICAgIGxhYmVsX3NpemUgPSAxOCwKICAgICAgICAgICAgICAgIGxhYmVscz1jKCIiLCdEJykpICsKICAgZ2dzYXZlKCJ+L21vdW50L2dkcml2ZS9taWxvL0ZpZ3VyZXMvbGl2ZXJfdjIvc3VwcGxfZmlnNy5wZGYiLCBoZWlnaHQgPSAxMywgd2lkdGg9MTQpICsKICBnZ3NhdmUoIn4vbW91bnQvZ2RyaXZlL21pbG8vRmlndXJlcy9saXZlcl92Mi9zdXBwbF9maWc3LnBuZyIsIGhlaWdodCA9IDEzLCB3aWR0aD0xNCkgCmBgYAo=